树形ListView

第一次接触树形ListView是一年前,当时公司做的项目是一个企业的员工管理软件,在展示员工时用到的,我花了大半天时间,才把树形ListView搞明白,完成任务后就没有然后了(当时主管还对我说要注意代码的积累,可我没在意)。今年五月份来北京找工作,找了两个月才找到工作(对这份工作还不能满意,没办法还得要吃饭),这两个月,浮躁的心也静下来了,做技术的就踏踏实实的把技术搞好,再去想其他的。废话不多说了说下这个树形ListView吧,做了些封装,只要明白原理,用的时候只需根据自己的需求修改些地方就可以了。
我封装的这个树形ListView针对数据是有从属关系的数据。
针对数据,
第一步:将数据通过反射和注解转化为想要的数据
第二步:理清数据关系(简单说,就是找出谁是谁的爹,谁是谁的儿)
第三步:将数据排序(要知道ListView是将数据源里的数据挨个展现出来的,所以顺序很重要)
这是针对数据的操作
怎么将树形层级展示出来呢?通过View 的位置的后移做到的,层级越高,放的距离越远离屏幕左方(一个固定距离*层级数)
如何实现展开和缩放(展开一些子节点就会展示出来,收缩一些数据就会隐藏)
通过两个数据源,一个数据源为显示的数据源,另一个为全部数据源,根据结点状态过滤出显示数据源。
上面这些就是树形ListView的大体思想
首先介绍下代码结构


MainActivity,SimpleTreeAdapter,FileNode只是些使用时的一些类,
Node,TreeHelper,TreeListViewAdapter等这些是我们封装的
首先说下Node,Node的类是我们封装的数据源,我们要将数据转化为Node类
	int id;
	int pid = 0;
	/**
	 *名称
	 */
	String label;
	/**
	 * 层级
	 */
	int level;
	/**
	 * 当前是否展开
	 */
	private boolean isExpand =false;
	int index;
	private int icon;
	Node parent;
	List<Node> children =  new ArrayList<Node>();
	
	public Node(int id, int pid, String label) {
		super();
		this.id = id;
		this.pid = pid;
		this.label = label;
	}
	
	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 getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
	/**
	 * 节点的层级
	 * @return
	 */
	public int getLevel() {
		return parent ==null?0:parent.getLevel()+1;
	}
	public void setLevel(int level) {
		this.level = level;
	}
	public boolean isExpand() {
		return isExpand;
	}
	public void setExpand(boolean isExpand) {
		this.isExpand = isExpand;
		if(!isExpand){
			for(Node n :children){
				n.setExpand(false);
			}
		}
	}
	public int getIcon() {
		return icon;
	}
	public void setIcon(int icon) {
		this.icon = icon;
	}
	public Node getParent() {
		return parent;
	}
	public void setParent(Node parent) {
		this.parent = parent;
	}
	public List<Node> getChildren() {
		return children;
	}
	public void setChildren(List<Node> children) {
		this.children = children;
	}
	/**
	 * 判断是否是根节点
	 */
	public boolean isRoot(){
		return parent == null;
	}
	/**
	 * 父节点是否展开
	 * @return
	 */
	public boolean isParentExpand(){
		if(parent == null)
			return true;
		return  parent.isExpand();
	}
	/**
	 * 是否是叶节点
	 * @return
	 */
	public boolean isLeaf(){
		return children.size() == 0;
	}
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
setExpand,getLevel这两个方法挺有意思的,主要是一种递归的思想
TreeHelper类是这个Tree ListView的核心思想
/**
	 * 将用户数据转化为Node
	 * @param datas
	 * @return
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	public static <T> List<Node> convertToNodes(List<T> datas) throws IllegalAccessException, IllegalArgumentException{
		List<Node> nodes = new ArrayList<Node>();
		Log.i("TAG", "convertToNodes data="+datas.size()+"");
		int index = 0;
		for(T t: datas){
			Node node  =null;
			Class clazz = t.getClass();
			Field[] fileds = clazz.getDeclaredFields();
			int id =-1;
			int pid=-1;
			String label=null;
			Log.i("TAG", "fileds.length="+fileds.length);
			
			for(Field f :fileds){
				if(f.getAnnotation(TreeNodeId.class)!=null){
					f.setAccessible(true);
					id = f.getInt(t);	
					Log.i("TAG", "通过反射注解获取到注解");
				}else{
					Log.i("TAG", "通过反射注解获取失败");
				}
				if(f.getAnnotation(TreeNodePid.class)!=null){
					f.setAccessible(true);
					pid = f.getInt(t);	
					Log.i("TAG", "通过反射注解获取到注解");
				}else{
					Log.i("TAG", "通过反射注解获取失败");
				}
				if(f.getAnnotation(TreeNodeLabel.class)!=null){
					f.setAccessible(true);
					label = (String) f.get(t);	
					Log.i("TAG", "convertToNodes label="+label+"");
					Log.i("TAG", "通过反射注解获取到注解");
					
				}else{
					Log.i("TAG", "通过反射注解获取失败");
				}
			}
			Log.i("TAG", "convertToNodes id,pid,label="+id+"-"+pid+"-"+label);
			node  = new Node(id, pid, label);
			node.setIndex(index);
			nodes.add(node);
			index++;
			
		}
	
		Log.i("TAG", "convertToNodes nodes="+nodes.size()+"");
		//关联关系
		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.getPid() == m.getId()){
					n.parent = m;
					m.getChildren().add(n);
				}
				if(n.getId()==m.getPid()){
					m.parent =n;
					n.getChildren().add(m);
				}
				
			}
		}
		//设置图片
		for(Node n :nodes){
			setNodeIcon(n);
			Log.i("TAG", "标签内容="+n.getLabel());
		}
		return nodes;
	}
	/**
	 * 设置图标
	 * @param n
	 */
	private static void setNodeIcon(Node n) {
		if(n.getChildren().size()>0&&n.isExpand()){
			n.setIcon(R.drawable.expand);
		}else if(n.getChildren().size()>0&&!n.isExpand()){
			n.setIcon(R.drawable.shrink);
			
		}else{
			n.setIcon(-1);
		}
		
	}
	public static <T> List<Node> getSortedNodes(List<T> datas,int defaultExpandLevel) throws IllegalAccessException, IllegalArgumentException{
		List<Node> result = new ArrayList<Node>();
		List<Node> nodes = convertToNodes(datas);
		List<Node> rootNodes = getRootNodes(nodes);
		for(Node node : rootNodes){
			addNode(result,node,defaultExpandLevel,1);
		}
		return result;
		
	}
	private static void addNode(List<Node> result, Node node,
			int defaultExpandLevel, int curLevel) {
		node.setLevel(curLevel);
		result.add(node);
		
		if(node.isLeaf())
			return ;
		if(defaultExpandLevel>=curLevel){
			node.setExpand(true);
			
		}else{
			node.setExpand(false);
		}
		
		for(Node n:node.getChildren()){
			addNode(result, n, defaultExpandLevel, curLevel+1);
			
		}
		
	}
	/**
	 * 过滤出要显示的
	 * @param nodes
	 * @return
	 */
	public static List<Node> filterVisbleNode(List<Node> nodes){
		List<Node> visbleNode = new ArrayList<Node>();
		for(Node n :nodes){
			if(n.isRoot()||n.isParentExpand()){
				setNodeIcon(n);
				visbleNode.add(n);
			}
		}
		return visbleNode;
		
	}
	/**
	 * 获取根节点
	 * @param nodes
	 * @return
	 */
	private static List<Node> getRootNodes(List<Node> nodes) {
		List<Node> rootNodes = new ArrayList<Node>();
		for(Node n :nodes){
			if(n.isRoot()){
				setNodeIcon(n);
				rootNodes.add(n);
			}
		}
		return rootNodes;
	}
包含了,数据的转化(反射和注释准备下篇文章在阐述),数据的排序,数据的排序包括认亲,递归获取数据,数据
的过滤
	protected Context context;
	protected List<Node> mAllNodes;
	protected List<Node> visbleNodes;
	protected LayoutInflater inflater;
	protected ListView mTree;
	public interface OnTreeNodeClickListener{
		void onClick(Node node,int position);
	}
	private OnTreeNodeClickListener mListener;
	public void setOnTreeNodeClickListener(OnTreeNodeClickListener mClickListener){
		this.mListener =mClickListener;
		
	}
   public <T> TreeListViewAdapter(ListView mTree, Context context,List<T> datas,int defaultExpandLevel) throws IllegalAccessException, IllegalArgumentException {
	// TODO Auto-generated constructor stub data
	   this.context = context;
	    mAllNodes =TreeHelper.getSortedNodes(datas, defaultExpandLevel);
	    visbleNodes = TreeHelper.filterVisbleNode(mAllNodes);
	    Log.i("TAG", "TreeListViewAdapter"+visbleNodes.size());
	    Log.i("TAG", "TreeListViewAdapter"+mAllNodes.size());
	    inflater = LayoutInflater.from(context);
	    this.mTree = mTree;
	    this.mTree.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				expandOrCollapse(position);
				if(mListener!=null){
					mListener.onClick(visbleNodes.get(position), position);
				}
				
				
			}
		});
   }
   /**
    * 点击收缩或展开
    * @param position
    */

	protected void expandOrCollapse(int position) {
	        Node n = visbleNodes.get(position);
	        if(n!=null){
	        	if(n.isLeaf()){
	        		return ;
	        	}
	        	n.setExpand(!n.isExpand());
	        	visbleNodes = TreeHelper.filterVisbleNode(mAllNodes);
	        	notifyDataSetChanged();
	        }
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return visbleNodes.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return visbleNodes.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Node n = visbleNodes.get(position);
		convertView = getConvertView(visbleNodes.get(position), position, convertView, parent);
		Log.i("---------", "getView="+visbleNodes.get(position).getLabel());
		convertView.setPadding(30*n.getLevel(), 3, 3, 3);
		return convertView;
	}
	public abstract View getConvertView(Node node ,int position,View convertView,ViewGroup parent);
这是将树形adapter封装一次,让外层调用者少操些ListView的心,
这三个类是树形ListView的核心类,在项目结构上看到的最后三个类是注释类,自己bean必须加上注释才能便于我解析,否则就无法解析,就不能正常转换数据。
望各位路过的神不吝赐教
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值