您的位置:首页 > 移动开发 > Android开发

android多级列表

2016-11-16 11:33 113 查看
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。

所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。





android中有ExpandListView控件,但是这个控件只支持两级列表。对于多级列表如果重写这个不是很好用。

实现这种列表 思想就是递归,构造一个子父级的关系。

话不多说 代码中体会

Activity

[java] view
plain copy

print?





package com.example.customtreeviewdemo;

import java.util.ArrayList;

import java.util.List;

import android.app.Activity;

import android.os.Bu
1fff7
ndle;

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这个里面

[java] view
plain copy

print?





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节点的操作 点击,选中改变 更新等

[java] view
plain copy

print?





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.On
20000
CheckedChangeListener;

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 模型类

[java] view
plain copy

print?





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这个也是核心操作类,主要功能是将业务数据和节点数据进行匹配

[java] view
plain copy

print?





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);

}

}

}

核心的代码就是这些,希望对大家有帮助。

转自:http://blog.csdn.net/shaoyezhangliwei/article/details/52275130
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 多级列表