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"));
}
}
以上就是自定义树形控件的所有代码,希望对大家的学习和工作有所帮助。