android多级树形列表
【摘要】
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。
所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。
...
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。
所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。
android中有ExpandListView控件,但是这个控件只支持两级列表。对于多级列表如果重写这个不是很好用。
实现这种列表 思想就是递归,构造一个子父级的关系。
话不多说 代码中体会
Activity
-
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;
-
}
-
-
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) {
-
-
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) {
-
e.printStackTrace();
-
} catch (IllegalAccessException e) {
-
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, "秋香"));
-
}
-
}
Adapter
这个adapter是继承了自己的定义的一个TreeListViewAdapter,核心实现都是在TreeListViewAdapter这个里面
-
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);
-
}
-
}
-
}
自定义TreeListViewAdapter 这个是整个树形结构的一个适配器,这里面主要是实现对Node节点的操作 点击,选中改变 更新等
-
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();
-
}
-
-
}
node 模型类
-
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;
-
}
-
-
-
}
TreeHelper这个也是核心操作类,主要功能是将业务数据和节点数据进行匹配
-
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);
-
}
-
}
-
-
}
核心的代码就是这些,希望对大家有帮助。
大家如果有其它问题,可以加入我的qq群:Android开发经验交流群 454430053 互相讨论交流。
文章来源: wukong.blog.csdn.net,作者:再见孙悟空_,版权归原作者所有,如需转载,请联系作者。
原文链接:wukong.blog.csdn.net/article/details/52275130
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)