android多级列表
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。
所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。
android中有ExpandListView控件,但是这个控件只支持两级列表。对于多级列表如果重写这个不是很好用。
实现这种列表 思想就是递归,构造一个子父级的关系。
话不多说 代码中体会
Activity
- package com.example.customtreeviewdemo;
- import java.util.ArrayList;
- import java.util.List;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ListView;
- import android.widget.Toast;
- import com.example.customtreeviewdemo.bean.MyNodeBean;
- import com.example.customtreeviewdemo.tree.Node;
- import com.example.customtreeviewdemo.tree.TreeListViewAdapter.OnTreeNodeClickListener;
- public class MainActivity extends Activity {
- private ListView treeLv;
- private Button checkSwitchBtn;
- private MyTreeListViewAdapter<MyNodeBean> adapter;
- private List<MyNodeBean> mDatas = new ArrayList<MyNodeBean>();
- //标记是显示Checkbox还是隐藏
- private boolean isHide = true;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initDatas();
- treeLv = (ListView) this.findViewById(R.id.tree_lv);
- checkSwitchBtn = (Button)this.findViewById(R.id.check_switch_btn);
- checkSwitchBtn.setOnClickListener(new OnClickListener(){
- @Override
- public void onClick(View v) {
- if(isHide){
- isHide = false;
- }else{
- isHide = true;
- }
- adapter.updateView(isHide);
- }
- });
- try {
- adapter = new MyTreeListViewAdapter<MyNodeBean>(treeLv, this,
- mDatas, 10, isHide);
- adapter.setOnTreeNodeClickListener(new OnTreeNodeClickListener() {
- @Override
- public void onClick(Node node, int position) {
- if (node.isLeaf()) {
- Toast.makeText(getApplicationContext(), node.getName(),
- Toast.LENGTH_SHORT).show();
- }
- }
- @Override
- public void onCheckChange(Node node, int position,
- List<Node> checkedNodes) {
- StringBuffer sb = new StringBuffer();
- for (Node n : checkedNodes) {
- int pos = n.getId() - 1;
- sb.append(mDatas.get(pos).getName()).append("---")
- .append(pos + 1).append(";");
- }
- Toast.makeText(getApplicationContext(), sb.toString(),
- Toast.LENGTH_SHORT).show();
- }
- });
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- treeLv.setAdapter(adapter);
- }
- private void initDatas() {
- mDatas.add(new MyNodeBean(1, 0, "中国古代"));
- mDatas.add(new MyNodeBean(2, 1, "唐朝"));
- mDatas.add(new MyNodeBean(3, 1, "宋朝"));
- mDatas.add(new MyNodeBean(4, 1, "明朝"));
- mDatas.add(new MyNodeBean(5, 2, "李世民"));
- mDatas.add(new MyNodeBean(6, 2, "李白"));
- mDatas.add(new MyNodeBean(7, 3, "赵匡胤"));
- mDatas.add(new MyNodeBean(8, 3, "苏轼"));
- mDatas.add(new MyNodeBean(9, 4, "朱元璋"));
- mDatas.add(new MyNodeBean(10, 4, "唐伯虎"));
- mDatas.add(new MyNodeBean(11, 4, "文征明"));
- mDatas.add(new MyNodeBean(12, 7, "赵建立"));
- mDatas.add(new MyNodeBean(13, 8, "苏东东"));
- mDatas.add(new MyNodeBean(14, 10, "秋香"));
- }
- }
Adapter
这个adapter是继承了自己的定义的一个TreeListViewAdapter,核心实现都是在TreeListViewAdapter这个里面
- package com.example.customtreeviewdemo;
- import java.util.List;
- import android.content.Context;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.CheckBox;
- import android.widget.CompoundButton;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.ImageView;
- import android.widget.ListView;
- import android.widget.TextView;
- import com.example.customtreeviewdemo.tree.Node;
- import com.example.customtreeviewdemo.tree.TreeListViewAdapter;
- public class MyTreeListViewAdapter<T> extends TreeListViewAdapter<T> {
- public MyTreeListViewAdapter(ListView mTree, Context context,
- List<T> datas, int defaultExpandLevel,boolean isHide)
- throws IllegalArgumentException, IllegalAccessException {
- super(mTree, context, datas, defaultExpandLevel,isHide);
- }
- @SuppressWarnings("unchecked")
- @Override
- public View getConvertView(Node node, int position, View convertView,
- ViewGroup parent) {
- ViewHolder viewHolder = null;
- if (convertView == null)
- {
- convertView = mInflater.inflate(R.layout.list_item, parent, false);
- viewHolder = new ViewHolder();
- viewHolder.icon = (ImageView) convertView
- .findViewById(R.id.id_treenode_icon);
- viewHolder.label = (TextView) convertView
- .findViewById(R.id.id_treenode_name);
- viewHolder.checkBox = (CheckBox)convertView.findViewById(R.id.id_treeNode_check);
- convertView.setTag(viewHolder);
- } else
- {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- if (node.getIcon() == -1)
- {
- viewHolder.icon.setVisibility(View.INVISIBLE);
- } else
- {
- viewHolder.icon.setVisibility(View.VISIBLE);
- viewHolder.icon.setImageResource(node.getIcon());
- }
- if(node.isHideChecked()){
- viewHolder.checkBox.setVisibility(View.GONE);
- }else{
- viewHolder.checkBox.setVisibility(View.VISIBLE);
- setCheckBoxBg(viewHolder.checkBox,node.isChecked());
- }
- viewHolder.label.setText(node.getName());
- return convertView;
- }
- private final class ViewHolder
- {
- ImageView icon;
- TextView label;
- CheckBox checkBox;
- }
- /**
- * checkbox是否显示
- * @param cb
- * @param isChecked
- */
- private void setCheckBoxBg(CheckBox cb,boolean isChecked){
- if(isChecked){
- cb.setBackgroundResource(R.drawable.check_box_bg_check);
- }else{
- cb.setBackgroundResource(R.drawable.check_box_bg);
- }
- }
- }
自定义TreeListViewAdapter 这个是整个树形结构的一个适配器,这里面主要是实现对Node节点的操作 点击,选中改变 更新等
- package com.example.customtreeviewdemo.tree;
- import java.util.ArrayList;
- import java.util.List;
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.BaseAdapter;
- import android.widget.CheckBox;
- import android.widget.CompoundButton;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.ListView;
- import android.widget.RelativeLayout;
- /**
- * tree适配器
- * @param <T>
- */
- public abstract class TreeListViewAdapter<T> extends BaseAdapter {
- protected Context mContext;
- /**
- * 存储所有可见的Node
- */
- protected List<Node> mNodes;
- protected LayoutInflater mInflater;
- /**
- * 存储所有的Node
- */
- protected List<Node> mAllNodes;
- /**
- * 点击的回调接口
- */
- private OnTreeNodeClickListener onTreeNodeClickListener;
- public interface OnTreeNodeClickListener {
- /**
- * 处理node click事件
- * @param node
- * @param position
- */
- void onClick(Node node, int position);
- /**
- * 处理checkbox选择改变事件
- * @param node
- * @param position
- * @param checkedNodes
- */
- void onCheckChange(Node node, int position,List<Node> checkedNodes);
- }
- public void setOnTreeNodeClickListener(
- OnTreeNodeClickListener onTreeNodeClickListener) {
- this.onTreeNodeClickListener = onTreeNodeClickListener;
- }
- /**
- *
- * @param mTree
- * @param context
- * @param datas
- * @param defaultExpandLevel
- * 默认展开几级树
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- */
- public TreeListViewAdapter(ListView mTree, Context context, List<T> datas,
- int defaultExpandLevel, boolean isHide)
- throws IllegalArgumentException, IllegalAccessException {
- mContext = context;
- /**
- * 对所有的Node进行排序
- */
- mAllNodes = TreeHelper
- .getSortedNodes(datas, defaultExpandLevel, isHide);
- /**
- * 过滤出可见的Node
- */
- mNodes = TreeHelper.filterVisibleNode(mAllNodes);
- mInflater = LayoutInflater.from(context);
- /**
- * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
- */
- mTree.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view,
- int position, long id) {
- expandOrCollapse(position);
- if (onTreeNodeClickListener != null) {
- onTreeNodeClickListener.onClick(mNodes.get(position),
- position);
- }
- }
- });
- }
- /**
- * 相应ListView的点击事件 展开或关闭某节点
- *
- * @param position
- */
- public void expandOrCollapse(int position) {
- Node n = mNodes.get(position);
- if (n != null)// 排除传入参数错误异常
- {
- if (!n.isLeaf()) {
- n.setExpand(!n.isExpand());
- mNodes = TreeHelper.filterVisibleNode(mAllNodes);
- notifyDataSetChanged();// 刷新视图
- }
- }
- }
- @Override
- public int getCount() {
- return mNodes.size();
- }
- @Override
- public Object getItem(int position) {
- return mNodes.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- final Node node = mNodes.get(position);
- convertView = getConvertView(node, position, convertView, parent);
- // 设置内边距
- convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
- if (!node.isHideChecked()) {
- //获取各个节点所在的父布局
- RelativeLayout myView = (RelativeLayout) convertView;
- //父布局下的CheckBox
- CheckBox cb = (CheckBox) myView.getChildAt(1);
- cb.setOnCheckedChangeListener(new OnCheckedChangeListener(){
- @Override
- public void onCheckedChanged(CompoundButton buttonView,
- boolean isChecked) {
- TreeHelper.setNodeChecked(node, isChecked);
- List<Node> checkedNodes = new ArrayList<Node>();
- for(Node n:mAllNodes){
- if(n.isChecked()){
- checkedNodes.add(n);
- }
- }
- onTreeNodeClickListener.onCheckChange(node,position,checkedNodes);
- TreeListViewAdapter.this.notifyDataSetChanged();
- }
- });
- }
- return convertView;
- }
- public abstract View getConvertView(Node node, int position,
- View convertView, ViewGroup parent);
- /**
- * 更新
- * @param isHide
- */
- public void updateView(boolean isHide){
- for(Node node:mAllNodes){
- node.setHideChecked(isHide);
- }
- this.notifyDataSetChanged();
- }
- }
node 模型类
- package com.example.customtreeviewdemo.bean;
- public class MyNodeBean {
- /**
- * 节点Id
- */
- private int id;
- /**
- * 节点父id
- */
- private int pId;
- /**
- * 节点name
- */
- private String name;
- /**
- *
- */
- private String desc;
- /**
- * 节点名字长度
- */
- private long length;
- public MyNodeBean(int id, int pId, String name) {
- super();
- this.id = id;
- this.pId = pId;
- this.name = name;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- public int getPid() {
- return pId;
- }
- public void setPid(int pId) {
- this.pId = pId;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getDesc() {
- return desc;
- }
- public void setDesc(String desc) {
- this.desc = desc;
- }
- public long getLength() {
- return length;
- }
- public void setLength(long length) {
- this.length = length;
- }
- }
TreeHelper这个也是核心操作类,主要功能是将业务数据和节点数据进行匹配
- package com.example.customtreeviewdemo.tree;
- import java.lang.reflect.Field;
- import java.util.ArrayList;
- import java.util.List;
- import com.example.customtreeviewdemo.R;
- public class TreeHelper {
- /**
- * 根据所有节点获取可见节点
- *
- * @param allNodes
- * @return
- */
- public static List<Node> filterVisibleNode(List<Node> allNodes) {
- List<Node> visibleNodes = new ArrayList<Node>();
- for (Node node : allNodes) {
- // 如果为根节点,或者上层目录为展开状态
- if (node.isRoot() || node.isParentExpand()) {
- setNodeIcon(node);
- visibleNodes.add(node);
- }
- }
- return visibleNodes;
- }
- /**
- * 获取排序的所有节点
- *
- * @param datas
- * @param defaultExpandLevel
- * @return
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- */
- public static <T> List<Node> getSortedNodes(List<T> datas,
- int defaultExpandLevel, boolean isHide)
- throws IllegalAccessException, IllegalArgumentException {
- List<Node> sortedNodes = new ArrayList<Node>();
- // 将用户数据转化为List<Node>
- List<Node> nodes = convertData2Nodes(datas, isHide);
- // 拿到根节点
- List<Node> rootNodes = getRootNodes(nodes);
- // 排序以及设置Node间关系
- for (Node node : rootNodes) {
- addNode(sortedNodes, node, defaultExpandLevel, 1);
- }
- return sortedNodes;
- }
- /**
- * 把一个节点上的所有的内容都挂上去
- */
- private static void addNode(List<Node> nodes, Node node,
- int defaultExpandLeval, int currentLevel) {
- nodes.add(node);
- if (defaultExpandLeval >= currentLevel) {
- node.setExpand(true);
- }
- if (node.isLeaf())
- return;
- for (int i = 0; i < node.getChildrenNodes().size(); i++) {
- addNode(nodes, node.getChildrenNodes().get(i), defaultExpandLeval,
- currentLevel + 1);
- }
- }
- /**
- * 获取所有的根节点
- *
- * @param nodes
- * @return
- */
- public static List<Node> getRootNodes(List<Node> nodes) {
- List<Node> rootNodes = new ArrayList<Node>();
- for (Node node : nodes) {
- if (node.isRoot()) {
- rootNodes.add(node);
- }
- }
- return rootNodes;
- }
- /**
- * 将泛型datas转换为node
- *
- * @param datas
- * @return
- * @throws IllegalArgumentException
- * @throws IllegalAccessException
- */
- public static <T> List<Node> convertData2Nodes(List<T> datas, boolean isHide)
- throws IllegalAccessException, IllegalArgumentException {
- List<Node> nodes = new ArrayList<Node>();
- Node node = null;
- for (T t : datas) {
- int id = -1;
- int pId = -1;
- String name = null;
- Class<? extends Object> clazz = t.getClass();
- Field[] declaredFields = clazz.getDeclaredFields();
- /**
- * 与MyNodeBean实体一一对应
- */
- for (Field f : declaredFields) {
- if ("id".equals(f.getName())) {
- f.setAccessible(true);
- id = f.getInt(t);
- }
- if ("pId".equals(f.getName())) {
- f.setAccessible(true);
- pId = f.getInt(t);
- }
- if ("name".equals(f.getName())) {
- f.setAccessible(true);
- name = (String) f.get(t);
- }
- if ("desc".equals(f.getName())) {
- continue;
- }
- if ("length".equals(f.getName())) {
- continue;
- }
- if (id == -1 && pId == -1 && name == null) {
- break;
- }
- }
- node = new Node(id, pId, name);
- node.setHideChecked(isHide);
- nodes.add(node);
- }
- /**
- * 比较nodes中的所有节点,分别添加children和parent
- */
- for (int i = 0; i < nodes.size(); i++) {
- Node n = nodes.get(i);
- for (int j = i + 1; j < nodes.size(); j++) {
- Node m = nodes.get(j);
- if (n.getId() == m.getpId()) {
- n.getChildrenNodes().add(m);
- m.setParent(n);
- } else if (n.getpId() == m.getId()) {
- n.setParent(m);
- m.getChildrenNodes().add(n);
- }
- }
- }
- for (Node n : nodes) {
- setNodeIcon(n);
- }
- return nodes;
- }
- /**
- * 设置打开,关闭icon
- *
- * @param node
- */
- public static void setNodeIcon(Node node) {
- if (node.getChildrenNodes().size() > 0 && node.isExpand()) {
- node.setIcon(R.drawable.tree_expand);
- } else if (node.getChildrenNodes().size() > 0 && !node.isExpand()) {
- node.setIcon(R.drawable.tree_econpand);
- } else
- node.setIcon(-1);
- }
- public static void setNodeChecked(Node node, boolean isChecked) {
- // 自己设置是否选择
- node.setChecked(isChecked);
- /**
- * 非叶子节点,子节点处理
- */
- setChildrenNodeChecked(node, isChecked);
- /** 父节点处理 */
- setParentNodeChecked(node);
- }
- /**
- * 非叶子节点,子节点处理
- */
- private static void setChildrenNodeChecked(Node node, boolean isChecked) {
- node.setChecked(isChecked);
- if (!node.isLeaf()) {
- for (Node n : node.getChildrenNodes()) {
- // 所有子节点设置是否选择
- setChildrenNodeChecked(n, isChecked);
- }
- }
- }
- /**
- * 设置父节点选择
- *
- * @param node
- */
- private static void setParentNodeChecked(Node node) {
- /** 非根节点 */
- if (!node.isRoot()) {
- Node rootNode = node.getParent();
- boolean isAllChecked = true;
- for (Node n : rootNode.getChildrenNodes()) {
- if (!n.isChecked()) {
- isAllChecked = false;
- break;
- }
- }
- if (isAllChecked) {
- rootNode.setChecked(true);
- } else {
- rootNode.setChecked(false);
- }
- setParentNodeChecked(rootNode);
- }
- }
- }
核心的代码就是这些,希望对大家有帮助。
转自:http://blog.csdn.net/shaoyezhangliwei/article/details/52275130
android多级列表相关推荐
- Android多级列表的实现
多级列表是ui入门,没什么特别需要注意的难点,直接上代码: 第一级列表窗口 /*** 第一级列表* 1.编辑状态下选中一个条目,会弹出底部操作栏* 2.非编辑状态下选中条目会跳转到下一级* 3.复选框 ...
- Android UI开发第二篇——多级列表(ExpandableListView)
开发中很多地方使用到了多级列表,android可以使用ExpandableListView很好的实现,下面模仿了手机qq的实现,见下图. 多级列表使用了ExpandableListView,自定义了A ...
- Android UI 之实现多级列表TreeView
所谓TreeView就是在Windows中常见的多级列表树,在Android中系统只默认提供了ListView和ExpandableListView两种列表,最多只支持到二级列表的实现,所以如果想要实 ...
- 更快实现Android多级树形选择列表
图片demo下载地址:https://download.csdn.net/download/Imshuyuan/85464136 快速实现Android多级树形列表,这个库是在鸿洋多级树形列表demo ...
- android--------ExpandableListView的使用多级列表
2019独角兽企业重金招聘Python工程师标准>>> 多级列表ExpandableListView 扩展列表能够显示一个指示在每项显示项的当前状态(状态通常是一个扩展的组,组的孩子 ...
- 安卓listView实现多级列表
安卓listView实现多级列表 实现两级列表我们可以使用ExpandableListView,但对于实现多级列表,使用ExpandableListView嵌套实现起来就比较费劲,可以使用listVi ...
- word2010多级列表编号变成黑块的解决方案
为什么80%的码农都做不了架构师?>>> 1.在文档最后键入多行文字,分别设置为和多级列表关联的样式.(依然显示黑块) 2.选择新键入的这部面文字,先选择多级列表的无,然后再选 ...
- word报错:题注或页码中不含章节编号。请使用“开始”选项卡上的“多级列表”按钮,然后选择链接到标题样式的编号方案(无法添加题注)
这破问题还真不好解决.... 看了这个视频:WORD教学五(论文排版之论文中的图表跟随章节插入题注) 貌似知道怎么弄了..这样操作: 选择标题,然后点击多级列表 --> 定义新的多级列表 然后在 ...
- word里的多级列表和项目编号是什么区别?
numbering:主要用于创建同级编号,对于正文内并列关系的段落增加编号. multilevel list:主要用于创建多层编号,还能和标题之类绑定,为不同层级的标题加编号.这个用的比较多,还能创建 ...
最新文章
- c语言各种编程风格 微软 gnu,编程规范-c语言的编程风格
- 如何在JavaScript中实现堆栈和队列?
- spring mvc DispatcherServlet详解之拾忆工具类utils
- P1476 休息中的小呆
- 敏捷水手——单体法到微服务之旅
- 中国移动MM7 API用户手册(七)
- android仿高德地图透明黑字,Android 仿高德地图可拉伸的BottomSheet
- 那些年破处倒闭的公司,都有哪些特征?打工人避雷必看
- jetty之嵌入式运行jetty
- 服务器与普通电脑的区别?
- Python利用SMTP发邮件
- 2018美赛B题总结
- UrlRewrite的使用
- modbus tcp调试助手_wemos D1 arduino项目实战1-TCP协议连接Tlink平台①
- html逐字显示并停止,Pr字幕如何让他逐字出现逐字消失?
- 计算机用通讯电压多少,通信局(站)用交流电源的质量指标要求
- win7 64位虚拟机安装
- oracle中distinct和count函数组合使用
- 聚英国际|【燎原计划启动周第二期AMA】Filecoin经济模型及热点争议解读
- WebStorm2016.2 注册码及激活,2018.6.14亲测有效