1.效果如图 ,老板的需求:
1.各种群按照树形目录展开
2.点击列表可以收缩展开
3.选中后进行筛选
2.分析下思路
- 每个数据都应该有个根节点,也就是上级目录。确定了上级目录就可以找到当前数据对应的位置。
2.为避免多重嵌套影响性能,这里的数据展示我们用一个列表足矣实现
3.实现的思路是item向右偏移
3.贴代码时间
Node节点对象的定义 项目中用到了环信SDK 环信部分直接忽略吧 QAQ
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class Node implements Serializable{
/**
* 节点id
*/
private int id;
/**
* 父节点id
*/
private int pId;
/**
* 是否展开
*/
private boolean isExpand = false;
private boolean isChecked = false;
private boolean isHideChecked = true;
/**
* 节点名字
*/
private String name;
/**
* 节点级别
*/
private int level;
/**
* 节点展示图标
*/
private int icon;
/**
* 节点所含的子节点
*/
private List<Node> childrenNodes = new ArrayList<Node>();
/**
* 节点的父节点
*/
private Node parent;
/**
* 环信群ID
*/
private String hxGroupId;
public Node() {
}
public Node(int id, int pId, String name,String hxGroupId) {
super();
this.id = id;
this.pId = pId;
this.name = name;
this.hxGroupId = hxGroupId;
}
public String getHxGroupId() {
return hxGroupId;
}
public void setHxGroupId(String hxGroupId) {
this.hxGroupId = hxGroupId;
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", pId=" + pId +
", isExpand=" + isExpand +
", isChecked=" + isChecked +
", isHideChecked=" + isHideChecked +
", name='" + name + '\'' +
", level=" + level +
", icon=" + icon +
", childrenNodes=" + childrenNodes +
", parent=" + parent +
", hxGroupId='" + hxGroupId + '\'' +
'}';
}
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;
}
}
帮助类TreeHelper
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
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;
String hxGroupId = 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 ("hxGroupId".equals(f.getName())) {
f.setAccessible(true);
hxGroupId = (String) f.get(t);
}
if ("desc".equals(f.getName())) {
continue;
}
if ("length".equals(f.getName())) {
continue;
}
if (id == -1 && pId == -1 && name == null&&hxGroupId==null) {
break;
}
}
node = new Node(id, pId, name,hxGroupId);
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.arrow_right);
} else if (node.getChildrenNodes().size() > 0 && !node.isExpand()) {
node.setIcon(R.drawable.arrow_down);
} 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);
}
}
}
适配器TreeListViewAdapter
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;
import java.util.ArrayList;
import java.util.List;
/**
* 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() * 56, 3, 3, 3);
if (!node.isHideChecked()) {
//获取各个节点所在的父布局
RelativeLayout myView = (RelativeLayout) convertView;
//父布局下的CheckBox
CheckBox cb = (CheckBox) myView.getChildAt(0);
//扩充可触摸的区域
//expandViewTouchDelegate(cb,50,50,50,50);
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();
}
activity中的主要代码
private void initView() {
initTreeData();
try {
adapter = new MyTreeListViewAdapter<>(treeLv, this,
mDatas, 0, isHide);
adapter.setOnTreeNodeClickListener(new TreeListViewAdapter.OnTreeNodeClickListener() {
@Override
public void onClick(Node node, int position) {
if (node.isLeaf()) {
}
}
@Override
public void onCheckChange(Node node, int position,
List<Node> checkedNodes) {
// TODO Auto-generated method stub
checkedNode = checkedNodes;
StringBuffer sb = new StringBuffer();
for (Node n : checkedNodes) {
sb.append(n.getName()).append(":").append(n.getHxGroupId());
}
Logger.d("选中的" + sb.toString());
}
});
adapter.updateView(isHide);
} 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 initTreeData() {
for (ChatGroupEntity.RDataBean bean : countTask.getRData()) {
mDatas.add(new MyNodeBean(Integer.valueOf(bean.getGroup_id()), Integer.valueOf(bean.getUp_group_id()), bean.getGroup_code(), bean.getHx_id()));
}
}
布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_height="match_parent">
<include layout="@layout/title_bar_common"/>
<com.fjrcloud.fuqing.util.customview.ClearEditText
android:id="@+id/filter_edit"
android:layout_width="match_parent"
android:layout_height="30dp"
android:focusableInTouchMode="true"
android:layout_marginTop="8dip"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:gravity="center_vertical"
android:layout_marginBottom="8dp"
android:paddingLeft="15dp"
android:drawablePadding="15dp"
android:background="@drawable/shape_search_view"
android:drawableLeft="@mipmap/icon_check_name"
android:hint="搜索"
android:singleLine="true"
android:textSize="14sp"/>
<TextView
android:background="#f1f1f1"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_15"/>
<ListView
android:layout_marginTop="15dp"
android:id="@+id/tree_lv"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
OK收工。有什么不懂得可以留言 联系我QQ :910689331
或者微博:Liberation_k
当然 源码在这哈
链接:http://pan.baidu.com/s/1c0KpS6 密码:q4xf