實現類似 QQ 聯繫人列表的分組效果:
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| ExpandableListView | 原生支持分組展開/收起、視圖復用、滾動性能好 | 自定義分組頭較繁瑣、動畫定制受限 | 標準二級列表、數據結構固定 |
| ListView 嵌套 ListView | 完全控制子項佈局、支持複雜嵌套 | 滾動衝突處理麻煩、內存佔用大、性能差 | 極度非標準佈局、需三級以上嵌套 |
| RecyclerView + Sectioned Adapter | 現代架構、靈活性最高、支持多類型 ViewHolder、動畫豐富 | 需引入第三方庫或自寫適配器、學習曲線稍陡 | 新項目、複雜列表、需 Material Design 效果 |
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);
}
// build.gradle
implementation 'com.github.luizgrp.sectionedrecyclerviewadapter:sectionedrecyclerviewadapter:2.0.0'
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();
}
}
getItemId 返回唯一標識,啟用 setHasStableIds(true)