首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android ListView getChildAt 返回 null 解決方案

     編輯:程式設計綜合問答

問題現象

在 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 個)
  • 索引超出可見範圍即返回 null
  • ListView 採用視圖回收機制,非可見項目不存在於視圖樹中

正確解決方案

方案一:僅操作可見項目(getChildCount)

mListView.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);
                }
            }
        }
    }
});

方案二:在 Adapter 內部管理狀態(推薦)

將展開/收合狀態存於資料模型,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;
}

方案三:配合 OnScrollListener 處理滑動隱藏

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) {}
});

關鍵 API 對照

方法返回值用途
getCount()資料總筆數資料層級操作
getChildCount()當前可見子視圖數視圖層級遍歷
getChildAt(index)可見範圍內第 index 個 View操作具體視圖
getPositionForView(View)該 View 對應的資料位置視圖轉資料索引
getFirstVisiblePosition()第一個可見項目位置計算實際索引

總結

getChildAt() 僅能存取當前可見的子視圖。正確做法是使用 getChildCount() 遍歷可見範圍,或(更推薦)在 Adapter 資料層管理狀態並配合 notifyDataSetChanged(),避免直接操作視圖導致的複雜度與 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved