当前位置: 首页 > article >正文

Android:任意层级树形控件(有效果图和Demo示例)

先上效果图:

1.创建treeview文件夹

2.treeview -> adapter -> SimpleTreeAdapter.java 

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;


import com.ybgc.acceptanceofconcealedwork.R;
import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.Node;
import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.TreeListViewAdapter;

import java.util.List;

public class SimpleTreeAdapter<T> extends TreeListViewAdapter<T> {
    //    private int mCurrentItem = -1;
    private ViewHolder viewHolder = null;
    private Context mContext;

    public SimpleTreeAdapter(ListView mTree, Context context, List<T> datas,
                             int defaultExpandLevel) throws IllegalArgumentException,
            IllegalAccessException {
        super(mTree, context, datas, defaultExpandLevel);
        mContext = context;
    }

    @Override
    public View getConvertView(Node node, int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.list_item, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.icon = convertView
                    .findViewById(R.id.id_treenode_icon);
            viewHolder.label = convertView
                    .findViewById(R.id.id_treenode_label);
            viewHolder.rlSelectItem = convertView
                    .findViewById(R.id.rlSelectItem);
            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());
        }

        viewHolder.label.setText(node.getName());

        return convertView;
    }

    private final class ViewHolder {
        private ImageView icon;
        private TextView label;
        private RelativeLayout rlSelectItem;
    }
}

3.treeview -> treebean

3.1.treeview -> treebean -> Node.java

import java.util.ArrayList;
import java.util.List;

public class Node
{

	private String id;
	/**
	 * 根节点pId为0
	 */
	private String pId = "0";

	private String name;


	/**
	 * 当前的级别
	 */
	private int level;

	/**
	 * 是否展开
	 */
	private boolean isExpand = false;

	private int icon;

	/**
	 * 下一级的子Node
	 */
	private List<Node> children = new ArrayList<Node>();

	/**
	 * 父Node
	 */
	private Node parent;

	public Node()
	{
	}

	public Node(String id, String pId, String name)
	{
		super();
		this.id = id;
		this.pId = pId;
		this.name = name;
	}

	public int getIcon()
	{
		return icon;
	}

	public void setIcon(int icon)
	{
		this.icon = icon;
	}

	public String getId()
	{
		return id;
	}

	public void setId(String id)
	{
		this.id = id;
	}

	public String getpId()
	{
		return pId;
	}

	public void setpId(String pId)
	{
		this.pId = pId;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}


	public void setLevel(int level)
	{
		this.level = level;
	}

	public boolean isExpand()
	{
		return isExpand;
	}

	public List<Node> getChildren()
	{
		return children;
	}

	public void setChildren(List<Node> children)
	{
		this.children = children;
	}

	public Node getParent()
	{
		return parent;
	}

	public void setParent(Node parent)
	{
		this.parent = parent;
	}

	/**
	 * 是否为跟节点
	 * 
	 * @return
	 */
	public boolean isRoot()
	{
		return parent == null;
	}

	/**
	 * 判断父节点是否展开
	 * 
	 * @return
	 */
	public boolean isParentExpand()
	{
		if (parent == null)
			return false;
		return parent.isExpand();
	}

	/**
	 * 是否是叶子节点
	 * 
	 * @return
	 */
	public boolean isLeaf()
	{
		return children.size() == 0;
	}

	/**
	 * 获取level
	 */
	public int getLevel()
	{
		return parent == null ? 0 : parent.getLevel() + 1;
	}

	/**
	 * 设置展开
	 * 
	 * @param isExpand
	 */
	public void setExpand(boolean isExpand)
	{
		this.isExpand = isExpand;
		if (!isExpand)
		{

			for (Node node : children)
			{
				node.setExpand(isExpand);
			}
		}
	}

}

3.2.treeview -> treebean -> TreeHelper.java

import com.ybgc.acceptanceofconcealedwork.R;
import com.ybgc.acceptanceofconcealedwork.util.LogUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class TreeHelper {
    /**
     * 传入我们的普通bean,转化为我们排序后的Node
     *
     * @param datas
     * @param defaultExpandLevel
     * @return
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> List<Node> getSortedNodes(List<T> datas,
                                                int defaultExpandLevel) {
        List<Node> result = new ArrayList<Node>();
        try {

            // 将用户数据转化为List<Node>
            List<Node> nodes = convetData2Node(datas);
            // 拿到根节点
            List<Node> rootNodes = getRootNodes(nodes);
            // 排序以及设置Node间关系
            for (Node node : rootNodes) {
                addNode(result, node, defaultExpandLevel, 1);
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }

        return result;
    }

    /**
     * 过滤出所有可见的Node
     *
     * @param nodes
     * @return
     */
    public static List<Node> filterVisibleNode(List<Node> nodes) {
        List<Node> result = new ArrayList<Node>();
        try {
            for (Node node : nodes) {
                // 如果为跟节点,或者上层目录为展开状态
                if (node.isRoot() || node.isParentExpand()) {
                    setNodeIcon(node);
                    result.add(node);
                }
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }


        return result;
    }

    /**
     * 将我们的数据转化为树的节点
     *
     * @param datas
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    private static <T> List<Node> convetData2Node(List<T> datas) {

        List<Node> nodes = new ArrayList<Node>();
        Node node = null;
        try {
            for (T t : datas) {
                String id = "-1";
                String pId = "-1";
                String label = null;
               
                Class<? extends Object> clazz = t.getClass();
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field f : declaredFields) {
                    if (f.getAnnotation(TreeNodeId.class) != null) {
                        f.setAccessible(true);
                        id = String.valueOf(f.get(t));
                    }
                    if (f.getAnnotation(TreeNodePid.class) != null) {
                        f.setAccessible(true);
                        pId = String.valueOf(f.get(t));
                    }
                    if (f.getAnnotation(TreeNodeLabel.class) != null) {
                        f.setAccessible(true);
                        label = (String) f.get(t);
                    }
                   
                    if (!id.equals("-1") && !pId.equals("-1") && label != null) {
                        break;
                    }
                }
                node = new Node(id, pId, label);
                nodes.add(node);
            }

            /**
             * 设置Node间,父子关系;让每两个节点都比较一次,即可设置其中的关系
             */
            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 (m.getpId().equals(n.getId())) {
                        n.getChildren().add(m);
                        m.setParent(n);
                    } else if (m.getId().equals(n.getpId())) {
                        m.getChildren().add(n);
                        n.setParent(m);
                    }
                }
            }

            // 设置图片
            for (Node n : nodes) {
                setNodeIcon(n);
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }

        return nodes;
    }

    private static List<Node> getRootNodes(List<Node> nodes) {

        List<Node> root = new ArrayList<Node>();
        try {
            for (Node node : nodes) {
                if (node.isRoot())
                    root.add(node);
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }
        return root;
    }

    /**
     * 把一个节点上的所有的内容都挂上去
     */
    private static void addNode(List<Node> nodes, Node node,
                                int defaultExpandLeval, int currentLevel) {
        try {
            nodes.add(node);
            if (defaultExpandLeval >= currentLevel) {
                node.setExpand(true);
            }

            if (node.isLeaf())
                return;
            for (int i = 0; i < node.getChildren().size(); i++) {
                addNode(nodes, node.getChildren().get(i), defaultExpandLeval,
                        currentLevel + 1);
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }

    }

    /**
     * 设置节点的图标
     *
     * @param node
     */
    private static void setNodeIcon(Node node) {
        try {
            if (node.getChildren().size() > 0 && node.isExpand()) {
                node.setIcon(R.mipmap.tree_ex);//设置展开状态图标
            } else if (node.getChildren().size() > 0 && !node.isExpand()) {
                node.setIcon(R.mipmap.tree_ec);//设置未展开状态图标
            } else {
                node.setIcon(-1);
            }
        } catch (Exception e) {
            LogUtil.e(e.toString());
        }

    }

}

3.3.treeview -> treebean -> TreeListViewAdapter.java

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.ListView;

import java.util.List;

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
	{
		void onClick(Node node, int position);
	}

	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) throws IllegalArgumentException,
			IllegalAccessException
	{
		mContext = context;
		/**
		 * 对所有的Node进行排序
		 */
		mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
		/**
		 * 过滤出可见的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(int position, View convertView, ViewGroup parent)
	{
		Node node = mNodes.get(position);
		convertView = getConvertView(node, position, convertView, parent);
		// 设置内边距
		convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
		return convertView;
	}

	public abstract View getConvertView(Node node, int position,
			View convertView, ViewGroup parent);

	
}

3.4.treeview -> treebean -> TreeNodeId.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodeId
{
}

3.5.treeview -> treebean -> TreeNodeLabel.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodeLabel
{

}

3.6.treeview -> treebean -> TreeNodePid.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TreeNodePid
{

}

4.treeview -> bean

4.1.treeview -> bean -> FileBean.java

import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.TreeNodeAcode;
import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.TreeNodeId;
import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.TreeNodeLabel;
import com.ybgc.acceptanceofconcealedwork.divview.treeview.treebean.TreeNodePid;

public class FileBean
{
	@TreeNodeId
	private String id;
	@TreeNodePid
	private String parentId;
	@TreeNodeLabel
	private String name;

	private long length;
	private String desc;

	public FileBean(String id, String parentId, String name,String acode)
	{
		super();
		this.id = id;
		this.parentId = parentId;
		this.name = name;
	}

}

5.layout布局文件

5.1.activity_main.xml 主文件布局(ListView就是树形控件的载体)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@+id/id_tree"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="#aaa"
        android:dividerHeight="1px" >
    </ListView>

</RelativeLayout>

5.2.ListView 的子布局list_item.xml(树形控件的子布局,里面的图片就是用来展示树形控件打开状态和关闭状态的图片)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:minHeight="40dip" >

    <ImageView
        android:id="@+id/id_treenode_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:src="@drawable/tree_ec" />

    <TextView
        android:id="@+id/id_treenode_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/id_treenode_icon"
        android:text="@string/app_name"
        android:textSize="18dip" />

</RelativeLayout>

6.上面树形控件代码已经完成,下面是页面中使用

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private List<FileBean> mDatas = new ArrayList<FileBean>();
    private ListView mTree;
    private TreeListViewAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initDatas();
        mTree = findViewById(R.id.id_tree);
        try {
            //适配器参数:tree控件,上下文,数据源,默认展开层级
            mAdapter = new SimpleTreeAdapter<FileBean>(mTree, this, mData, 0);//初始化适配器
            mAdapter.setOnTreeNodeClickListener(new TreeListViewAdapter.OnTreeNodeClickListener() {//点击事件监听
                @Override
                public void onClick(Node node, int position) {
                    if (node.isLeaf()) {
                        Toast.makeText(getApplicationContext(), node.getName(),
                                Toast.LENGTH_SHORT).show();
                    }
                }

            });

        } catch (Exception e) {
            e.printStackTrace();
        }
        mTree.setAdapter(mAdapter);//设置数据
    }

    /**
     * 初始化数据
     */
    private void initData() {
        mDatas.add(new FileBean("1", "0", "文件管理系统"));
        mDatas.add(new FileBean("2", "1", "游戏"));
        mDatas.add(new FileBean("3", "1", "文档"));
        mDatas.add(new FileBean("4", "1", "程序"));
        mDatas.add(new FileBean("5", "2", "使命召唤"));
        mDatas.add(new FileBean("6", "2", "绝地求生"));
        mDatas.add(new FileBean("7", "4", "面向对象"));
        mDatas.add(new FileBean("8", "4", "非面向对象"));
        mDatas.add(new FileBean("9", "7", "C++"));
        mDatas.add(new FileBean("10", "7", "JAVA"));
        mDatas.add(new FileBean("11", "7", "Javascript"));
        mDatas.add(new FileBean("12", "8", "C"));
        mDatas.add(new FileBean("13", "3", "测试文档1"));
        mDatas.add(new FileBean("14", "3", "测试文档2"));
    }
}

以上就是自定义树形控件的所有代码,希望对大家的学习和工作有所帮助。


http://www.kler.cn/a/401866.html

相关文章:

  • Spring、SpringMVC、SpringBoot、Mybatis小结
  • 初级数据结构——栈与队列的互相实现
  • 基于普中51单片机开发板的电子门铃设计( proteus仿真+程序+设计报告+讲解视频)
  • 【分割评价指标-nnUNet V2训练】- AutoDL
  • 解决Ubuntu18.04及以上版本高分辨率下导致字体过小问题
  • Ubuntu 22.04.4 LTS + certbot 做自动续签SSL证书(2024-11-14亲测)
  • 项目-摄像
  • python学习笔记1
  • 基于深度学习的文本信息提取方法研究(pytorch python textcnn框架)
  • 机器学习笔记 // 度量ML预测的准确率
  • Marin说PCB之电源完整性之电源网络的PDN仿真CST---04
  • 如何确保爬取的数据准确性和完整性?
  • 完整http服务器
  • 单片机智能家居火灾环境安全检测-分享
  • Modbus TCP转Modbus ASCII解决方案
  • 2、PyTorch张量的运算API(上)
  • 经验笔记:从生成 SSH 密钥到成功连接测试(以Gitee为例)
  • 微软Office 2021 24年11月授权版
  • c语言金典100题“从入门到放弃”10-15
  • Dubbo自定义扩展注册中心
  • Jav项目实战II基于微信小程序的助农扶贫的设计与实现(开发文档+数据库+源码)
  • 数据结构(二)线性表
  • 助力模型训练,深度学习的经典数据集介绍
  • Matplotlib | 理解直方图中bins表示的数据含义
  • WPF 中 MultiConverter ——XAML中复杂传参方式
  • 推荐一款UI/UX原型设计工具:Icons8 Lunacy