在 OnItemClickListener 中遍歷 ListView 所有子視圖控制顯示/隱藏,但 mListView.getChildAt(i) 返回 null 導致空指標崩潰。
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View v, int position, long id) {
for (int i = 0; i < mListView.getCount(); i++) { // 錯誤:用 getCount()
View callLogView = mListView.getChildAt(i); // 超出可見範圍返回 null
LinearLayout mRelativeLayout = (LinearLayout) callLogView.findViewById(R.id.myShow);
// ... 控制顯示隱藏
}
}
});
ListView.getCount() 返回資料總筆數(可能上百筆)ListView.getChildAt(index) 僅返回當前螢幕可見範圍內的子視圖(通常 5-10 個)nullmListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View v, int position, long id) {
// 正確:使用 getChildCount() 而非 getCount()
for (int i = 0; i < mListView.getChildCount(); i++) {
View childView = mListView.getChildAt(i);
if (childView != null) {
LinearLayout layout = childView.findViewById(R.id.myShow);
// 判斷是否為點擊項目:比較 position 與 child 的實際位置
int childPosition = mListView.getPositionForView(childView);
if (childPosition == position) {
layout.setVisibility(View.VISIBLE);
} else {
layout.setVisibility(View.GONE);
}
}
}
}
});
將展開/收合狀態存於資料模型,notifyDataSetChanged() 刷新:
// 資料模型
public class ItemData {
public String title;
public boolean isExpanded = false;
}
// Adapter 內部
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.item, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ItemData item = getItem(position);
holder.title.setText(item.title);
holder.expandLayout.setVisibility(item.isExpanded ? View.VISIBLE : View.GONE);
convertView.setOnClickListener(v -> {
item.isExpanded = !item.isExpanded;
notifyDataSetChanged(); // 或 notifyItemChanged(position)
});
return convertView;
}
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState != SCROLL_STATE_IDLE) {
// 滑動時隱藏所有展開項目
for (int i = 0; i < view.getChildCount(); i++) {
View child = view.getChildAt(i);
View expandLayout = child.findViewById(R.id.myShow);
expandLayout.setVisibility(View.GONE);
}
// 同步更新資料模型
for (ItemData item : dataList) item.isExpanded = false;
}
}
@Override public void onScroll(AbsListView view, int first, int count, int total) {}
});
| 方法 | 返回值 | 用途 |
|---|---|---|
getCount() | 資料總筆數 | 資料層級操作 |
getChildCount() | 當前可見子視圖數 | 視圖層級遍歷 |
getChildAt(index) | 可見範圍內第 index 個 View | 操作具體視圖 |
getPositionForView(View) | 該 View 對應的資料位置 | 視圖轉資料索引 |
getFirstVisiblePosition() | 第一個可見項目位置 | 計算實際索引 |
getChildAt() 僅能存取當前可見的子視圖。正確做法是使用 getChildCount() 遍歷可見範圍,或(更推薦)在 Adapter 資料層管理狀態並配合 notifyDataSetChanged(),避免直接操作視圖導致的複雜度與 Bug。