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

Android ListView 分組顯示完整方案:ExpandableListView 與嵌套列表實戰

     編輯:程式設計疑難解答

需求分析

實現類似 QQ 聯繫人列表的分組效果:

  • 分組項:設備評分、設備維修(含申請/接單)、設備保養(含申請/接單)
  • 分組頭無額外提示,純粹作為視覺分隔
  • 支持組內項目點擊、長按等交互

方案對比

方案優點缺點適用場景
ExpandableListView原生支持分組展開/收起、視圖復用、滾動性能好自定義分組頭較繁瑣、動畫定制受限標準二級列表、數據結構固定
ListView 嵌套 ListView完全控制子項佈局、支持複雜嵌套滾動衝突處理麻煩、內存佔用大、性能差極度非標準佈局、需三級以上嵌套
RecyclerView + Sectioned Adapter現代架構、靈活性最高、支持多類型 ViewHolder、動畫豐富需引入第三方庫或自寫適配器、學習曲線稍陡新項目、複雜列表、需 Material Design 效果

方案一:ExpandableListView 實現(推薦快速上手)

數據模型


public class DeviceGroup {
    String groupName;           // 分組標題(可選,不顯示時為空)
    List children;  // 子項列表
    boolean isExpanded;         // 默認展開狀態
}

public class DeviceItem {
    String title;      // "設備維修申請"
    int iconRes;       // 圖標資源
    int type;          // 業務類型標識
}

適配器實現


public class DeviceExpandableAdapter extends BaseExpandableListAdapter {
    private List groups;
    private LayoutInflater inflater;

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

    @Override
    public int getChildrenCount(int groupPosition) { 
        return groups.get(groupPosition).children.size(); 
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, 
                             View convertView, ViewGroup parent) {
        // 分組頭:若無提示需求,可返回空 View 或極薄分割線
        if (convertView == null) {
            convertView = new View(parent.getContext());
            convertView.setLayoutParams(new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, 0)); // 高度 0
        }
        return convertView;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, 
                             boolean isLastChild, View convertView, ViewGroup parent) {
        ViewHolder holder;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_device, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        
        DeviceItem item = groups.get(groupPosition).children.get(childPosition);
        holder.bind(item);
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true; // 子項可點擊
    }
    
    // ... 其他必需方法
}

關鍵屬性設置


ExpandableListView listView = findViewById(R.id.elv_devices);
listView.setGroupIndicator(null);           // 隱藏默認展開箭頭
listView.setDivider(null);                  // 移除分割線
listView.setChildDivider(null);             // 移除子項分割線
listView.setAdapter(adapter);

// 默認展開所有分組
for (int i = 0; i < adapter.getGroupCount(); i++) {
    listView.expandGroup(i);
}

方案二:RecyclerView + SectionedRecyclerViewAdapter(現代最佳實踐)

依賴庫


// build.gradle
implementation 'com.github.luizgrp.sectionedrecyclerviewadapter:sectionedrecyclerviewadapter:2.0.0'

Section 實現


public class DeviceSection extends StatelessSection {
    private List items;
    private String headerTitle;

    public DeviceSection(String headerTitle, List items) {
        super(R.layout.section_header, R.layout.item_device);
        this.headerTitle = headerTitle;
        this.items = items;
    }

    @Override
    public int getContentItemsTotal() { return items.size(); }

    @Override
    public RecyclerView.ViewHolder getItemViewHolder(View view) {
        return new ItemViewHolder(view);
    }

    @Override
    public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
        ((ItemViewHolder) holder).bind(items.get(position));
    }

    @Override
    public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
        return new HeaderViewHolder(view);
    }

    @Override
    public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
        HeaderViewHolder h = (HeaderViewHolder) holder;
        // 需求:分組頭無提示 → 隱藏或設置高度 0
        h.itemView.setVisibility(View.GONE); 
        // 或 h.itemView.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, 0));
    }
}

組裝


SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();

sectionAdapter.addSection(new DeviceSection("", repairItems));      // 設備維修組
sectionAdapter.addSection(new DeviceSection("", maintenanceItems)); // 設備保養組
sectionAdapter.addSection(new DeviceSection("", ratingItems));      // 設備評分組

recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(sectionAdapter);

進階:支持分組展開/收起動畫


public class ExpandableDeviceSection extends StatelessSection {
    private boolean expanded = true;
    
    @Override
    public int getContentItemsTotal() { 
        return expanded ? items.size() : 0; 
    }
    
    public void toggle() { 
        expanded = !expanded; 
        // 通知適配器局部刷新
        sectionAdapter.notifyDataSetChanged(); 
    }
}

性能優化建議

  1. 視圖復用:無論哪種方案,必須正確實現 ViewHolder 模式
  2. 數據分頁:若單組超過 50 項,考慮分頁加載或虛擬化
  3. 異步綁定:圖片加載使用 Glide/Picasso,避免阻塞 UI 線程
  4. 穩定 ID:實現 getItemId 返回唯一標識,啟用 setHasStableIds(true)

選型建議

新項目:首選 RecyclerView + SectionedAdapter,擴展性最強,符合現代架構。
維舊項目/簡單需求:ExpandableListView 開發效率最高,代碼量最少。
極度複雜嵌套:考慮 Epoxy 或 Groupie 等高級框架。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved