2016年11月13日 第二篇
Android自定义树形控件
注:根据鸿洋的Android自定义任意层级树形控件 编写
效果图
节点类(Node.java)
package jfsl.treeviewdemo.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by JFSL on 2016/9/1 15:27.
*/
public class Node
{
private int id;
/**
* 父节点
*/
private int pId;
/**
* 显示的文字内容
*/
private String name;
/**
* 层级
*/
private int level;
/**
* 是否展开
*/
private boolean isExpand;
private int iconId;
/**
* 父节点
*/
private Node parent;
/**
* 子节点
*/
private List<Node> children = new ArrayList<Node>();
public Node(int id,int pId,String name)
{
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;
}
/**
* 获取层级
* @return
*/
public int getLevel()
{
return parent == null ? 0 : parent.getLevel() + 1;
}
public void setLevel(int level)
{
this.level = level;
}
public boolean isExpand()
{
return isExpand;
}
/**
* 设置收缩状态
* @param expand
*/
public void setExpand(boolean expand)
{
isExpand = expand;
//设置收缩状态
if(!isExpand)
{
//所有子节点都设置成false
for(Node node : children)
{
node.setExpand(isExpand);
}
}
}
public int getIconId()
{
return iconId;
}
public void setIconId(int iconId)
{
this.iconId = iconId;
}
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;
}
/**
* 判断父节点的展开状态
*/
public boolean isParentExpand()
{
//根节点
if(parent == null)
return false;
return parent.isExpand();
}
/**
* 是否是叶子结点
*/
public boolean isLeaf()
{
return children.size() == 0;
}
}
节点辅助类(TreeViewHelper.java)
package jfsl.treeviewdemo.utils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import jfsl.treeviewdemo.R;
import jfsl.treeviewdemo.annotation.TreeNodeId;
import jfsl.treeviewdemo.annotation.TreeNodeLabel;
import jfsl.treeviewdemo.annotation.TreeNodeParentId;
/**
* Created by JFSL on 2016/9/1 15:25.
*
*/
public class TreeViewHelper
{
public static final int ICON_NONE = - 1;
public static final int CURRENT_LEVEL = 1;
/**
* 转换数据
* @param datas
* @param <T>
* @return
* @throws IllegalAccessException
*/
public static <T> List<Node> convertDatasToNodes(List<T> datas) throws IllegalAccessException
{
List<Node> nodes = new ArrayList<>();
Node node = null;
//遍历数据
for(T t : datas)
{
int id = - 1;
int pId = - 1;
String label = null;
/**
* 反射+注解 获取成员变量的值
*/
Class clazz = t.getClass();
Field[] fields = clazz.getDeclaredFields();
//反射获取值
for(Field field : fields)
{
//获取id
if(field.getAnnotation(TreeNodeId.class) != null)
{
//设置可见
field.setAccessible(true);
id = field.getInt(t);
}
//获取pId
if(field.getAnnotation(TreeNodeParentId.class) != null)
{
//设置可见
field.setAccessible(true);
pId = field.getInt(t);
}
//获取label
if(field.getAnnotation(TreeNodeLabel.class) != null)
{
//设置可见
field.setAccessible(true);
label = (String)field.get(t);
}
}
//加入节点
node = new Node(id,pId,label);
nodes.add(node);
}
//设置节点之间的关系
settingNodeRelation(nodes);
//设置图标状态
settingNodeIcon(nodes);
return nodes;
}
/**
* 设置节点之间的关系
* @param nodes
*/
private static void settingNodeRelation(List<Node> nodes)
{
//设置关系
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);
//n的父节点的id等于m节点的id;也就是m节点是n的父节点
if(n.getpId() == m.getId())
{
m.getChildren().add(n);
n.setParent(m);
}
//同理
else if(m.getpId() == n.getId())
{
n.getChildren().add(m);
m.setParent(n);
}
}
}
}
/**
* 设置图标状态
* @param nodes
*/
private static void settingNodeIcon(List<Node> nodes)
{
for(Node no : nodes)
{
setNodeIcon(no);
}
}
/**
* 设置节点图标
*
* @param node
*/
public static void setNodeIcon(Node node)
{
//有子节点
if(node.getChildren().size() > 0)
{
//展开
if(node.isExpand())
{
node.setIconId(R.mipmap.icon_expand);
return;
}
//收缩
node.setIconId(R.mipmap.icon_collapse);
}
//没有图标
else
node.setIconId(ICON_NONE);
}
/**
* 获取排序后的节点
*
* @param datas
* @param <T>
* @return
*/
public static <T> List<Node> getSortedNodes(List<T> datas,int defaultLevel) throws IllegalAccessException
{
//已经排序后的节点
List<Node> newNodes = new ArrayList<>();
//未排序的节点,只是转换的
List<Node> oldNodes = convertDatasToNodes(datas);
/**
* 类似于采用深度遍历树
* PS:另外的方法,前序遍历树
*/
//***********************************************************
//获取根节点
List<Node> rootNodes = getRootNodes(oldNodes);
for(Node node : rootNodes)
{
//默认级别为1
addNode(newNodes,node,defaultLevel,CURRENT_LEVEL);
}
//***********************************************************
return newNodes;
}
/**
* 深度遍历,并添加
*
* @param newNodes
* @param node
* @param defaultLevel
* @param currentLevel
*/
private static void addNode(List<Node> newNodes,Node node,int defaultLevel,int currentLevel)
{
//添加节点
newNodes.add(node);
//设置展开的级别
if(defaultLevel >= currentLevel)
{
node.setExpand(true);
}
//叶子节点,也就是最后了,不用再遍历
if(node.isLeaf())
return;
//遍历子节点
for(int i = 0;i < node.getChildren().size();i++)
{
addNode(newNodes,node.getChildren().get(i),defaultLevel,currentLevel + 1);
}
}
/**
* 过滤出需要显示的节点
*/
public static List<Node> filterVisibleNodes(List<Node> nodes)
{
List<Node> visibleNodes = new ArrayList<>();
for(Node node : nodes)
{
if(node.isRoot() || node.isParentExpand())
{
//设置节点的图标
setNodeIcon(node);
visibleNodes.add(node);
}
}
return visibleNodes;
}
/**
* 获取根节点
*
* @param oldNodes
* @return
*/
private static List<Node> getRootNodes(List<Node> oldNodes)
{
List<Node> rootNode = new ArrayList<>();
for(Node node : oldNodes)
{
if(node.isRoot())
{
rootNode.add(node);
}
}
return rootNode;
}
}
文件Bean类,测试的数据类(FileBean.java)
package jfsl.treeviewdemo.bean;
import jfsl.treeviewdemo.annotation.TreeNodeId;
import jfsl.treeviewdemo.annotation.TreeNodeLabel;
import jfsl.treeviewdemo.annotation.TreeNodeParentId;
/**
* Created by JFSL on 2016/9/1 15:22.
*/
public class FileBean
{
@TreeNodeId
private int id;
@TreeNodeParentId
private int pId;
@TreeNodeLabel
private String name;
public FileBean(int id,int pId,String name)
{
this.id = id;
this.pId = pId;
this.name = name;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override
public String toString()
{
return "FileBean{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
注解接口
1.TreeNodeId
package jfsl.treeviewdemo.annotation;
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
{
}
2.TreeNodeLabel
package jfsl.treeviewdemo.annotation;
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.TreeNodeParentId
package jfsl.treeviewdemo.annotation;
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 TreeNodeParentId
{
}
1.数据适配器(父类)(TreeViewAdapter.java)
package jfsl.treeviewdemo.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import java.util.List;
import jfsl.treeviewdemo.utils.Node;
import jfsl.treeviewdemo.utils.TreeViewHelper;
public abstract class TreeViewAdapter<T> extends BaseAdapter
{
//上下文
protected Context mContext;
//所有的节点
protected List<Node> mAllNodes;
//显示的节点
protected List<Node> mVisibleNodes;
//加载布局
protected LayoutInflater mInflater;
//ListView
protected ListView mListTree;
/**
* 自定义回调接口
*/
private OnTreeNodeClickListener mListener;
public interface OnTreeNodeClickListener
{
void onClick(Node node,int position);
}
public void setListener(OnTreeNodeClickListener listener)
{
mListener = listener;
}
public TreeViewAdapter(Context context,ListView listTree ,List<T> datas,int defaultLevel) throws IllegalAccessException
{
mContext = context;
mAllNodes = TreeViewHelper.getSortedNodes(datas,defaultLevel);
mVisibleNodes = TreeViewHelper.filterVisibleNodes(mAllNodes);
mInflater = LayoutInflater.from(mContext);
mListTree = listTree;
//设置ListView的点击事件
mListTree.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> adapterView,View view,int position,long id)
{
//展开或者收缩
expandOrCollapse(position);
//事件回调
if(mListener != null)
{
mListener.onClick(mVisibleNodes.get(position),position);
}
}
});
}
/**
* 展开或者收缩
* @param position
*/
private void expandOrCollapse(int position)
{
Node node = mVisibleNodes.get(position);
if(node != null)
{
if(node.isLeaf())
return;
//反向选择
node.setExpand(!node.isExpand());
//更新数据
mVisibleNodes = TreeViewHelper.filterVisibleNodes(mAllNodes);
notifyDataSetChanged();
}
}
@Override
public int getCount()
{
return mVisibleNodes.size();
}
@Override
public Object getItem(int position)
{
return mVisibleNodes.get(position);
}
@Override
public long getItemId(int position)
{
return position;
}
@Override
public View getView(int position,View view,ViewGroup viewGroup)
{
Node node = mVisibleNodes.get(position);
view = getConvertView(node,position,view,viewGroup);
//设置距离问题
view.setPadding(node.getLevel() * 70,3,3,3);
return view;
}
public abstract View getConvertView(Node node,int position,View view,ViewGroup viewGroup);
}
2.树形数据适配器(SimpleTreeAdapter.java)
package jfsl.treeviewdemo.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.List;
import jfsl.treeviewdemo.R;
import jfsl.treeviewdemo.utils.Node;
/**
* Created by JFSL on 2016/9/1 20:36.
* 自定义Item的界面
* 可以根据情况修改
*/
public class SimpleTreeAdapter<T> extends TreeViewAdapter<T>
{
public SimpleTreeAdapter(Context context,ListView listTree,List<T> datas,int defaultLevel) throws IllegalAccessException
{
super(context,listTree,datas,defaultLevel);
}
/**
* 主要用来自定义界面
* @param node
* @param position
* @param view
* @param viewGroup
* @return
*/
@Override
public View getConvertView(Node node,int position,View view,ViewGroup viewGroup)
{
ViewHolder holder = null;
if(view == null)
{
view = mInflater.inflate(R.layout.listview_item_treeview,viewGroup,false);
holder = new ViewHolder();
holder.icon = (ImageView)view.findViewById(R.id.id_item_icon);
holder.text = (TextView)view.findViewById(R.id.id_item_text);
view.setTag(holder);
} else
{
holder = (ViewHolder)view.getTag();
}
//没有图标,也就是没有子节点的数据项
if(node.getIconId() == - 1)
{
holder.icon.setVisibility(View.INVISIBLE);
}
//有图标
else
{
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(node.getIconId());
}
holder.text.setText(node.getName());
return view;
}
private class ViewHolder
{
ImageView icon;
TextView text;
}
}
测试的Activity
package jfsl.treeviewdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import jfsl.treeviewdemo.adapter.SimpleTreeAdapter;
import jfsl.treeviewdemo.bean.FileBean;
/**
* 主界面
* @date 2016年9月1日 21:27:11
* @author JFSL
*
*/
public class ActivityMain extends AppCompatActivity
{
//
private ListView mListView;
//数据集
private List<FileBean> mDatas;
//适配器
private SimpleTreeAdapter<FileBean> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try
{
mListView = (ListView)findViewById(R.id.id_listview);
//初始化模拟数据
initTestDatas();
//初始化适配器 默认显示层级 0
mAdapter = new SimpleTreeAdapter<FileBean>(this,mListView,mDatas,0);
//绑定适配器
mListView.setAdapter(mAdapter);
} catch(IllegalAccessException e)
{
}
}
/**
* 初始化模拟数据
*/
private void initTestDatas()
{
mDatas = new ArrayList<>();
FileBean bean = new FileBean(1,0,"xx小学");
mDatas.add(bean);
bean = new FileBean(2,1,"一(1)班");
mDatas.add(bean);
bean = new FileBean(201,2,"Android学习");
mDatas.add(bean);
bean = new FileBean(211,2,"Java技术");
mDatas.add(bean);
bean = new FileBean(221,2,"计算机网络");
mDatas.add(bean);
bean = new FileBean(8,0,"xx中学");
mDatas.add(bean);
bean = new FileBean(11,8,"九(2)班");
mDatas.add(bean);
bean = new FileBean(12,0,"xx中学");
mDatas.add(bean);
bean = new FileBean(13,12,"高一(16)班");
mDatas.add(bean);
bean = new FileBean(14,12,"高二(13)班");
mDatas.add(bean);
bean = new FileBean(15,12,"高三(11)班");
mDatas.add(bean);
bean = new FileBean(16,0,"XXXXXX");
mDatas.add(bean);
bean = new FileBean(17,16,"大一");
mDatas.add(bean);
bean = new FileBean(18,16,"大二");
mDatas.add(bean);
bean = new FileBean(19,16,"大三");
mDatas.add(bean);
bean = new FileBean(20,19,"Android学习");
mDatas.add(bean);
bean = new FileBean(21,19,"Java技术");
mDatas.add(bean);
bean = new FileBean(22,21,"计算机网络");
mDatas.add(bean);
}
}
1.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/id_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
2.listview_item_treeview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="56dp">
<ImageView
android:id="@+id/id_item_icon"
android:layout_width="56dp"
android:scaleType="center"
android:class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="@mipmap/ic_launcher"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/id_item_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_marginLeft="4dp"/>
</LinearLayout>
代码地址 http://114.215.186.20/14551100130/treeviewdemo.zip
点击查看更多内容
2人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦