逐步实现ListView嵌套ExpandableLayoutListView
来源:互联网 发布:intent传递数据 编辑:程序博客网 时间:2024/06/17 23:08
最近在尝试做一个多层嵌套ListView,先根据日期划分成各个子Item,子Item内再嵌套一个ListView展示该日期下的详细列表,最后列表的具体项能够通过点击来展开、收起以聚焦用户的注意力。先上效果图:
接下来我们开始逐步实现:
- 首先是布局文件,从内到外共有三个布局:交易记录、单日交易列表、整体交易列表。
可扩展布局用的是GitHub上大神的ExpandableLayout,Android
Studio 直接Gradle引入编译会报错,建议直接下载工程引用。
<!--交易记录--><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:expandable="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <xu.shallow.multi_listview.Expandable.ExpandableLayoutItem android:id="@+id/eli_row" android:layout_width="match_parent" android:layout_height="wrap_content" expandable:el_contentLayout="@layout/view_data_item_content" expandable:el_headerLayout="@layout/view_data_item_header"/></LinearLayout><!--单日交易列表--><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/data_list_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginTop="10dp" android:background="@android:color/holo_red_light" android:gravity="center" android:paddingBottom="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_horizontal_margin" android:textColor="@android:color/white"/> <xu.shallow.multi_listview.Expandable.ExpandableLayoutListView android:id="@+id/elv_data" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/data_list_title"/></RelativeLayout><!--整体交易列表--><ListView android:id="@+id/lv_main" android:layout_width="match_parent" android:layout_height="match_parent"></ListView>
- 布局文件完成之后我们开始生成测试数据并绑定到ListView上去
//手动生成测试用数据 List<TransInfo> list=makeDebugData(); ListView lv_main = (ListView)findViewById(R.id.lv_main); Main_Adapter main_adapter = new Main_Adapter(MainActivity.this, list); lv_main.setAdapter(main_adapter);
- 在主列表的Adapter中将列表按日期进行分类,并放入Map中
private Context context; private List<TransInfo> list; private Map<String, List<TransInfo>> listMap; private String[] date_keys; public Main_Adapter(Context context, List<TransInfo> list) { this.context = context; this.list = list; date_keys = getList(list); } … public void getList(List<TransInfo> list) { if (list == null || list.size() == 0) { date_keys = new String[0]; return; } listMap = new HashMap<>(); try { for (TransInfo transInfo : list) { String time = transInfo.getOrder_time(); SimpleDateFormat spf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); Date format_date = spf.parse(time); spf = new SimpleDateFormat("yyyy年MM月dd日", Locale.getDefault()); String date = spf.format(format_date); if (listMap.containsKey(date)) { listMap.get(date).add(transInfo); } else { List<TransInfo> tmpList = new ArrayList<>(); tmpList.add(transInfo); listMap.put(date, tmpList); } } date_keys = listMap.keySet().toArray(new String[listMap.size()]); Arrays.sort(date_keys, Collections.reverseOrder()); } catch (Exception e) { e.printStackTrace(); } } … @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.view_data_list_item, parent, false); } TextView data_list_title = BaseViewHolder.get(convertView, R.id.data_list_title); final ExpandableLayoutListView elv_data = BaseViewHolder.get(convertView, R.id.elv_data); data_list_title.setText(date_keys[position]); Data_List_Adapter adapter = new Data_List_Adapter(context, listMap.get(date_keys[position])); elv_data.setAdapter(adapter); return convertView; }
- 子列表中展示具体的记录
@Overridepublic View getView(final int position, View convertView, final ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate( R.layout.view_data_item, parent, false); } final ExpandableLayoutItem eli_row = BaseViewHolder.get(convertView, R.id.eli_row); TextView goods_name = BaseViewHolder.get(convertView, R.id.goods_name); TextView goods_amount = BaseViewHolder.get(convertView, R.id.goods_amount); TextView order_id = BaseViewHolder.get(convertView, R.id.order_id); TextView order_time = BaseViewHolder.get(convertView, R.id.order_time); goods_name.setText(list.get(position).getGoods_name()); String amount = list.get(position).getGoods_amount(); String fmt_amount = String.valueOf(Float.valueOf(amount) / 100); goods_amount.setText("¥ " + fmt_amount); order_id.setText(context.getString(R.string.order_id) + ":" + list.get(position).getOrder_id()); order_time.setText(context.getString(R.string.order_time) + ":" + list.get(position).getOrder_time()); return convertView;}
好布局和代码都写好了,让我们策马崩腾跑起来~
我们生成的测试数据是3组每组10个,运行下来却发现每组只显示了一个,查了下资料发现默认情况下Android是禁止在ScrollView中放入另外的ScrollView的,它的高度是无法计算的。为了解决这个问题,我们需要根据ListView的子项目重新计算主ListView的高度,然后把高度再作为LayoutParams设置给主ListView,这样它的高度就正确了。
public static void setListViewHeightBasedOnChildren(ListView listView) { try { ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { // pre-condition return; } int totalHeight = 0; for (int i = 0; i < listAdapter.getCount(); i++) { View listItem = listAdapter.getView(i, null, listView); listItem.measure(0, 0); totalHeight += listItem.getMeasuredHeight(); } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); } catch (Exception e) { e.printStackTrace(); }}
只要在设置ListView的Adapter后调用此静态方法即可让子ListView正确的显示在其父ListView的ListItem中。但是要注意的是,子ListView的每个Item必须是LinearLayout,不能是其他的,因为其他的Layout(如RelativeLayout)没有重写onMeasure(),所以会在onMeasure()时抛出异常。
配置好后我们再来看下效果:
我们发现子ListView已经可以正常展现出来并可以点击展开具体的项,但有一个问题,就是当我们子项展开的时候,ListView的高度并没有变化,需要下拉才能实现,会导致整个界面会有两个下拉列表,对用户体验并不是很好。那么我们需要重新处理下ListView的高度计算,对于屏幕之外的Item直接通过getView来计算高度,对于展示在屏幕内的Item我们实时计算目前的高度。
if (i >= listView.getFirstVisiblePosition() && i <= listView.getLastVisiblePosition()) { totalHeight += listView.getChildAt(i).getMeasuredHeight();} else { View listItem = listAdapter.getView(i, null, listView); listItem.measure(0, 0); totalHeight += listItem.getMeasuredHeight();}
同时,我们需要在Item内添加一个监听器,在展开及收起的时候通知ListView高度已经改变了。
eli_row.setOnExpendItemChangeListener(new ExpandableLayoutItem.onExpendItemChangeListener() { @Override public void onChange(boolean b) { Util.setListViewHeightBasedOnChildren((ListView) parent); }});
至此整个ListView的基本做好了,后续还需要完善的是在子Listview边界进行切换时,界面会重新计算高度,滚动条和界面会有一个短暂的卡顿。
最后附上项目地址:Multi_ListView
- 逐步实现ListView嵌套ExpandableLayoutListView
- 不重写listView实现 嵌套(listView嵌套listView)
- Android Listview嵌套Listview实现评论
- ListView嵌套ListView实现论坛评论效果
- Android ListView嵌套ListView的实现方式
- 使用ExpandableListView实现ListView嵌套ListView
- ScrollerView嵌套listview.实现,listview滑动
- Android ListView嵌套ListView的实现方式
- Android Listview嵌套Listview实现评论
- Listview嵌套Listview的实现方法
- Listview嵌套Listview实现评论效果
- listView 逐步优化
- ListView中嵌套checkbox实现多选
- listview中嵌套viewpager的实现总结
- scrollview嵌套listview的一种实现方式
- listview中嵌套viewpager的实现总结
- listview中嵌套viewpager的实现总结
- listview实现微信朋友圈嵌套
- AutoMapper用法
- OCJP题库知识点总结(4)
- Qt中常见的一些错误
- 1055. 集体照 (25)
- asp.net identity(微软首推的身份验证)2.0分析-基于vs2015默认程序
- 逐步实现ListView嵌套ExpandableLayoutListView
- Java高级编程-JUC
- |BZOJ 2028|平衡树|[SHOI2009]会场预约
- 编译工具
- TIJ......(一)
- Zookeeper安装
- JVM的Perm区持续增长导致OOM问题记录
- gcc和g++
- 详解JavaScript操作URL的方法(单页应用常用)