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

Android ExpandableListView 二級列表報錯:數據結構與適配器不匹配,改用 Map<String, List<?>> 即可解決

     編輯:程式設計疑難解答

ExpandableListView 實現二級列表時,常見崩潰:ClassCastException、NullPointerException、數據顯示錯亂。根因多為數據結構與適配器期望不一致。本文還原排查過程,給出標準數據模型與適配器實現。

錯誤現場

Logcat 截圖顯示數據結構與適配器解析邏輯不匹配(具體異常信息被圖片遮擋,但典型為類型轉換失敗)。

數據結構截圖 報錯代碼行截圖

核心問題:數據模型不標準

BaseExpandableListAdapter 要求:

  • Group 數據: List<String> groupList(或自定義對象列表)。
  • Child 數據: Map<String, List<ChildBean>> childMap,Key 必須與 Group 列表對應項一致(通常用 Group 名稱或 ID)。

原代碼可能直接用 List<List<ChildBean>> 或自定義對象嵌套,導致 getChild() 取值時類型轉換失敗。

修正後的標準數據結構

// 1. Group 列表
List<String> groupList = new ArrayList<>();
groupList.add("群組1");
groupList.add("群組2");

// 2. Child Map:Key = Group 名稱,Value = 該群組下的子項列表
Map<String, List<ChildBean>> childMap = new LinkedHashMap<>();

List<ChildBean> children1 = new ArrayList<>();
children1.add(new ChildBean("子項1-1"));
children1.add(new ChildBean("子項1-2"));
childMap.put("群組1", children1);

List<ChildBean> children2 = new ArrayList<>();
children2.add(new ChildBean("子項2-1"));
childMap.put("群組2", children2);

適配器關鍵方法實現

public class MyExpandableAdapter extends BaseExpandableListAdapter {
    private List<String> groupList;
    private Map<String, List<ChildBean>> childMap;
    private Context context;

    public MyExpandableAdapter(Context context, List<String> groupList, Map<String, List<ChildBean>> childMap) {
        this.context = context;
        this.groupList = groupList;
        this.childMap = childMap;
    }

    @Override
    public int getGroupCount() { return groupList.size(); }

    @Override
    public int getChildrenCount(int groupPosition) {
        String key = groupList.get(groupPosition);
        return childMap.get(key).size();
    }

    @Override
    public Object getGroup(int groupPosition) { return groupList.get(groupPosition); }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        String key = groupList.get(groupPosition);
        return childMap.get(key).get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) { return groupPosition; }

    @Override
    public long getChildId(int groupPosition, int childPosition) { return childPosition; }

    @Override
    public boolean hasStableIds() { return true; }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        // ... 充氣 group_item.xml,綁定 groupList.get(groupPosition)
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        String key = groupList.get(groupPosition);
        ChildBean bean = childMap.get(key).get(childPosition);
        // ... 充氣 child_item.xml,綁定 bean
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) { return true; }
}

避坑清單

  1. Key 唯一性: Group 名稱重複會導致 Child 覆蓋,建議用 Group ID 作為 Key。
  2. 空群組處理: childMap.put(group, new ArrayList<>()) 而非 null,防 NPE。
  3. 異步更新: 數據變更後必須 notifyDataSetChanged(),且在 UI 線程。
  4. 視覺回收: convertView 復用邏輯要正確,使用 ViewHolder 模式。

現代替代方案

  • RecyclerView + Groupie / Epoxy / ConcatAdapter: 更靈活、動畫豐富、支援異構列表。
  • Jetpack Compose: 聲明式 UI,LazyColumn + items 輕鬆實現展開/收起。

記住:ExpandableListView 的靈魂是「Group List + Child Map」,數據結構對齊了,90% 的崩潰與顯示異常自動消失。

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