android多级树形列表

举报
再见孙悟空_ 发表于 2022/01/13 00:09:21 2022/01/13
2k+ 0 0
【摘要】 我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。 所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。            ...

我们开发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);
      		}
      	}
      }
  
 

核心的代码就是这些,希望对大家有帮助。

DEMO源码

大家如果有其它问题,可以加入我的qq群:Android开发经验交流群 454430053  互相讨论交流。

文章来源: wukong.blog.csdn.net,作者:再见孙悟空_,版权归原作者所有,如需转载,请联系作者。

原文链接:wukong.blog.csdn.net/article/details/52275130

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。