Android 实现自己定义多级树控件和全选与反选的效果

时间:2023-03-09 03:06:28
Android  实现自己定义多级树控件和全选与反选的效果

博文開始之前,首先要感谢大牛:(lmj623565791),本博文是在其博文http://blog.****.net/lmj623565791/article/details/40212367基础上进一步的改动而来。

本博文主要是利用ListView实现多级树控件,并通过CheckBox来对各节点的全选与反选的功能,首先来看一下效果:

Android  实现自己定义多级树控件和全选与反选的效果

对于多级树的显示事实上就是通过数据中各个节点的关系,通过不同的缩进来达到树的效果。而数据中主要要把握id,父节点pId。name的关系,来显示其效果。

代码实现例如以下:

一. 布局xml文件

1.主界面activity_main.xml,简单的ListView和一个控制CheckBox切换的Button

<span style="font-family:KaiTi_GB2312;font-size:18px;"><LinearLayout 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"
android:orientation="vertical"
>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/check_switch_btn"
android:text="CheckBox切换"
android:gravity="center"
android:layout_marginBottom="10dp"
/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/tree_lv"
/>
</LinearLayout>
</span>

2.树节点布局list_view.xml. 树节点关闭和打开的Image,旋转框CheckBox,内容TextView

<span style="font-family:KaiTi_GB2312;font-size:18px;"><?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_econpand" /> <CheckBox
android:id="@+id/id_treeNode_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/id_treenode_icon"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@drawable/check_box_bg"
android:button="@null"
android:focusable="false"
/> <TextView
android:id="@+id/id_treenode_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/id_treeNode_check"
android:text="@string/app_name"
android:textSize="18sp" /> </RelativeLayout></span>

二.java代码实现

1.实体类MyNodeBean,内容能够依据详细情况增加

<span style="font-family:KaiTi_GB2312;font-size:18px;">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;
} }
</span>

2.主函数MainActivity.java

<span style="font-family:KaiTi_GB2312;font-size:18px;">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;
}
//切换checkbox
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) {
// TODO Auto-generated method stub 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) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
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, "秋香"));
}
}
</span>

3.ListView实现适配器MyTreeListViewAdapter.java

该适配器继承TreeListViewAdapter<T>,在该适配器中仅仅实现方法

<span style="font-family:KaiTi_GB2312;font-size:18px;">getConvertView(Node node, int position, View convertView,
ViewGroup parent)</span>

其它。父类中载入

<span style="font-family:KaiTi_GB2312;font-size:18px;">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);
}
}
}
</span>

三、树节点实现的方法

1.树节点实体类Node.java

<span style="font-family:KaiTi_GB2312;font-size:18px;">package com.example.customtreeviewdemo.tree;

import java.util.ArrayList;
import java.util.List; public class Node {
/**
* 节点id
*/
private int id;
/**
* 父节点id
*/
private int pId;
/**
* 是否展开
*/
private boolean isExpand = false;
private boolean isChecked = false;//是否选中
private boolean isHideChecked = true;//CheckBox是否隐藏
/**
* 节点名字
*/
private String name;
/**
* 节点级别
*/
private int level;
/**
* 节点展示图标
*/
private int icon;
/**
* 节点所含的子节点
*/
private List<Node> childrenNodes = new ArrayList<Node>();
/**
* 节点的父节点
*/
private Node parent; public Node() {
} public Node(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 boolean isExpand() {
return isExpand;
} /**
* 当父节点收起。其子节点也收起
* @param isExpand
*/
public void setExpand(boolean isExpand) {
this.isExpand = isExpand;
if (!isExpand) { for (Node node : childrenNodes) {
node.setExpand(isExpand);
}
}
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getLevel() {
return parent == null ? 0 : parent.getLevel() + 1;
} public void setLevel(int level) {
this.level = level;
} public int getIcon() {
return icon;
} public void setIcon(int icon) {
this.icon = icon;
} public List<Node> getChildrenNodes() {
return childrenNodes;
} public void setChildrenNodes(List<Node> childrenNodes) {
this.childrenNodes = childrenNodes;
} public Node getParent() {
return parent;
} public void setParent(Node parent) {
this.parent = parent;
} /**
* 推断是否是根节点
*
* @return
*/
public boolean isRoot() {
return parent == null;
} /**
* 推断是否是叶子节点
*
* @return
*/
public boolean isLeaf() {
return childrenNodes.size() == 0;
} /**
* 推断父节点是否展开
*
* @return
*/
public boolean isParentExpand()
{
if (parent == null)
return false;
return parent.isExpand();
} public boolean isChecked() {
return isChecked;
} public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
} public boolean isHideChecked() {
return isHideChecked;
} public void setHideChecked(boolean isHideChecked) {
this.isHideChecked = isHideChecked;
}
}
</span>

2.自己定义继承于BaseAdapter的适配器TreeListViewAdapter<T>

<span style="font-family:KaiTi_GB2312;font-size:18px;">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();
} }
</span>

3.Node节点实现帮助类TreeHelper.java

<span style="font-family:KaiTi_GB2312;font-size:18px;">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);
}
} }
</span>

以上就是本文的全部内容。

源代码地址:http://download.****.net/detail/a123demi/8109643