Android多级列表动态加载(三级及三级以上)
通常这种多级列表更多见于web端,移动端常见为省份选择之类的情况(类似QQ分组的通常只有两级),由于手机屏幕的限制,相当一部分也选择多个页面显示,所以也许用得不是很多,但俗话说,只有想不到的需求,没有不被要求到的需求,所以以防万一吧……
首先,最常见的为类似于省市这种形式的列表,特征是在展示前你可以获得全部数据,包括子列表,这也是最常见的多级列表形式。谷歌官方有提供ExpandableListView,可以实现两级列表,我个人以为,也这表明谷歌本身也只推荐手机端只实现两级的缩进(纯吐槽:坦白说以手机屏幕的宽度来看,在同一页面通过缩进来显示两级以上多级的层级关系真是毫无美感可言)。多级列表网上有一些实现方式,主要是前两种,都是展示前可以获得全部数据的,第三种是动态获取子列表数据时的实现方式,如下:
第一种方式:以单个listView实现,纯粹依赖缩进来造成层级效果
这种实现方式实质是以一个ListView列表来实现,无论是子列表数据还是父列表数据,展示时实际是处于同一级的,只是通过给子数据添加缩进,人为的制造出层级关系,看起来像是多级列表而已。取根列表数据资源存于代码中的firstLayerList列表,全部列表数据资源于totalList列表,但列表中每一项并不是直接的数据资源,而是数据本身、层级、数据本身id、数据父id、是否有子数据、是否已展开的一个封装对象。如:列表为List<Data>,数据为Data(String dataStr,int layer,long dataId,long dataParentId,boolean hasSubDatas,boolean isExpand);
在getView()方法中注意两点,一是根据子列表的层级计算并添加缩进,二是注意可见性,因为在convertView复用过程中有可能会复用可见性与当前项不同的view。
然后就是OnItemClickListener()方法,分三种情况:
- 若是点击项无子数据,点击不响应,return。
- 若是点击项有子数据并已展开,则响应为隐藏子数据,具体实现方式为从当前项下一项开始,以当前列表总项数为上限循环,对当前项之后的每一项作层级判断,如果layer值大于当前点击的position项的layer值,则说明为当前项的子数据、子子数据、子子子数据等,一直遍历到某一项layer值小于或等于当前选中position项的layerw值时break出循环,将遍历到的这些数据存入toDelList列表,然后通过removeAll(...)从当前列表的数据列表firstLayerList中移除toDelList列表中的所有数据项,调用notifyDataSetChanged()方法刷新列表。
- 若是点击项有子数据但未展开,则响应为展示子数据,具体实现方式为取当前点击项的dataId,遍历全部数据列表totalList,取每一项的dataParentId与当前点击项dataId比较,如果相等则说明为需要展示的子数据(当然,只是下一级子节点,好在需求也只是下一级),分别存入当前展示数据列表中的第position + i项,i从1开始累加,遍历完totalList列表后,刷新列表,会显示为展开子列表。
这种方式虽可实现,但比较繁复,且ExpandableListView本身提供的一些方法又不能直接用了,不推荐。
第三种,动态获取数据实现三级列表
这种针对的是在展示之前无法获取到子数据的情况,就是必须点击父数据项才去请求子数据的情况。
在我的示例里,拿到的作务列表中该任务数据中是包含有子任务数的,即下例中的getSubs()方法得到的b子任务数。
公司项目,只能截取代码片段了,adapter,subAdapter,grandSubAdapter是同一类的不同对象实现。 /**
* 任务列表监听
*/
private AdapterView.OnItemClickListener taskItemListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (0 != ((Task) (mAdapter.getItem((int) id))).getSubs()) {//在没有表头的前提下,(int)id和position是一样的效果,只是个人习惯
subListView = (ListView) view.findViewById(R.id.sub_task_list);//在adapter的xml布局里,最下方包含一个<ListView ...../>,默认为隐藏状态View.GONE
//subListView.setAdapter(subAdapter);
if (View.VISIBLE == subListView.getVisibility()) {//当子listView可见时,说明是展开状态,点击时的响应就应该是关闭
subListView.setVisibility(View.GONE);
} else {//子listView不可见,则展开子列表
subListView.setVisibility(View.VISIBLE);
subListView.setFocusable(false);
subListView.setOnItemClickListener(subTaskItemListener);
requestSubListDatas(((Task)(mAdapter.getItem((int)id))).getId());//参数为当前任务项id,请求该任务的子任务数据 //这里我是以Message形式传递给handler处理,为了便于阅读,就放在这里吧 subAdapter.setmTasks(子任务列表数据); ViewGroup.LayoutParams parentLp = subListView.getLayoutParams(); //这里可添加判断,只需计算一次即可 measureView = subAdapter.getView(0,null,subListView);//measureView为一个View对象 //注意在这里要求Adapter的布局文件中的listView控件的父容器为LinearLayout布局,否则会报错 measureView.measure(0,0); parentLp.height = measureView.getMeasuredHeight() * subAdapter.getCount(); subListView.setLayoutParams(parentLp);
subListView.setAdapter(subAdapter); }
}
}
};
/**
* 子任务列表监听
*/
private AdapterView.OnItemClickListener subTaskItemListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View subView, int position, long subId) {
if (0 != ((Task) (subAdapter.getItem((int) subId))).getSubs()) {
grandSubListView = (ListView) subView.findViewById(R.id.sub_task_list);
//grandSubListView.setAdapter(grandSubAdapter);
if (View.VISIBLE == grandSubListView.getVisibility()) {
grandSubListView.setVisibility(View.GONE);
//grandSubListView.setLayoutParams(subParentParams);
ViewGroup.LayoutParams mParentParams = subListView.getLayoutParams();
mParentParams.height -= grandSubListView.getLayoutParams().height;
subListView.setLayoutParams(mParentParams);
} else {
grandSubListView.setVisibility(View.VISIBLE);
grandSubListView.setFocusable(false);
//与以上任务列表监听相似,就不重复了
}
}
}
};
实现起来也不难,最主要是需在子列表展开前计算出子列表的高度以提供给子列表展示的空间。
- 上一篇:没有了
- 下一篇:没有了