首页 > 系统 > Android > 正文

Android 绘制多级树形选择列表实例代码

2019-10-22 18:15:38
字体:
来源:转载
供稿:网友

一、概述

前段时间有个项目的需要在Android端显示一个复选的多层树形控件,主要展示一个公司的组织架构,类似总部下面有各个部门,部门之下是组和员工等。另外需要加上展开与回收部门详情、关闭部分已开展的布局、勾选等功能。

效果图如下:

android,绘制树形列表,多级树形选择列表

二、思路分析

毫无疑问,对于这种数据可能达到几千几万行的列表视图,我们需要选择recyclerview等具有回收item功能的控件,因此Item的状态保持放在Model中而不是View中。

由于原始数据是树形结构的,我们需要先将树形结构转换为列表数据,类似根结点 - 父节点1 - 子结点1 - 子节点2 - 父节点2......这种形式 - 这恰恰是树的前序遍历

android,绘制树形列表,多级树形选择列表

实现思路 - 为了更简洁明白,左右颠倒处理

三、具体实现

简单的节点实现

public abstract class SimpleTreeNode {//层级protected int hierarchy;//父节点protected K parent = null;//子节点protected final List<K> children = new ArrayList<>();protected boolean isSelected;  // 是否被选中protected boolean isExpand;   // 是否展开}前序遍历则发生在adapter的getItem和getItemCount的时候public T getItem(int position) {  int[] cur = {position};  return getNode(topGroups, cur);}/** * 先序遍历 - 获取指定位置的节点 * * @param nodes  nodes * @param position itemPosition 数组只是为了实现手动box实现共享position * @return MultiSelectNode or null */ protected T getNode(List<T> nodes, final int[] position) {  for (T node : nodes) {    if (position[0] == 0) {      return node;    }    position[0]--;    if (node.getChildren().size() > 0) {      T finalNode = getNode(node.getChildren(), position);      if (finalNode != null) {        return finalNode;      }    }  }  return null;} /** * 先序遍历 - 获取展示的总长度 (isExpand = true) * * @param nodes nodes * @return int */protected int getTreeSize(List<T> nodes) {  int size = 0;  for (T node : nodes) {    size++;    size += getTreeSize(node.getChildren());    }  return size;}

对于如何实现展开和收缩的功能,我尝试了两种方式:

在渲染item的时候判断node.isExpand = false时,对item进行Gone处理,实际处理发现列表卡顿非常严重:假设所有的item都是隐藏的,那么因为列表没有显示全,所有的item都会进行渲染一遍....

数据遍历的时候将非展开的数据过滤掉:这种方式完美可行,只需要修改下遍历方法即可

protected int getTreeSize(List<T> nodes) { int size = 0; for (T node : nodes) {   size++;   // 展开过滤   if (node.isExpand()) {     size += getTreeSize(node.getChildren());   } } return size; } protected T getNode(List<T> nodes, final int[] position) { for (T node : nodes) {   if (position[0] == 0) {     return node;   }   position[0]--;   // 展开过滤   if (node.isExpand() && node.getChildren().size() > 0) {     T finalNode = getNode(node.getChildren(), position);     if (finalNode != null) {       return finalNode;     }   } } return null; }

以上多级树形列表的展开与隐藏便完成了,剩下的便是对树节点的一些操作:例如一个item展开的时候对其他同级item隐藏;一个item被勾选或取消勾选的时候改变其父节点和子节点的状态等。对于这些操作,我采用了类似Motion Event的方式 - 用事件传递与分发来处理。

比如展开的时候同级的item隐藏,其实便是通知兄弟节点设置expand为false。

android,绘制树形列表,多级树形选择列表

通知兄弟节点

勾选的操作稍麻烦,可能需要递归通知父节点检查更新,以及递归通知子节点勾选操作,取消勾选亦如此。

关键代码如下

/** * Class: SimpleTreeNode * Author: zwgg * Date: 2017/10/16 * Time: 10:35 * 简单的树节点模板类 * 这个自限定泛型可能有点费解:用于以基类导出类作为自身的泛型,以实现模板功能 * 例如:ClassNameA extend SimpleTreeNode< ClassNameA , T > * @see Enum */ public abstract class SimpleTreeNode<K extends SimpleTreeNode<K, T>, T extends TreeNodeEvent> {  //层级  protected int hierarchy;  //父节点  protected K parent = null;  //子节点  protected final List<K> children = new ArrayList<>();  public SimpleTreeNode() {  }  public SimpleTreeNode(int hierarchy) {    this.hierarchy = hierarchy;  }  public void bindingParent(K parent) {    this.parent = parent;  }  public void bindingChild(K child) {    this.children.add(child);  }  public void bindingChildren(List<K> children) {    this.children.clear();    this.children.addAll(children);  }  public void dataBinding(K parent, K child) {    parent.bindingChild(child);    child.bindingParent(parent);  }  public int getHierarchy() {    return hierarchy;  }  public void setHierarchy(int hierarchy) {    this.hierarchy = hierarchy;  }  /**   * 通知父节点   * @param event event   */  public void notifyParent(T event) {    if (parent != null) {      event.setNotifyType(TreeNodeEvent.NOTIFY_PARENT);      parent.onEvent(event);    }  }  /**   * 通知子节点   * @param event event   */  public void notifyChildren(T event) {    event.setNotifyType(TreeNodeEvent.NOTIFY_CHILDREN);    for (K child : children) {      child.onEvent(event);    }  }  /**   * 通知兄弟节点 - 需要具有相同的Parent   * @param event event   */  public void notifyBrother(T event) {    if (parent != null) {      event.setNotifyType(TreeNodeEvent.NOTIFY_BROTHER);      for (K child : parent.children) {        if (child != this) {          child.onEvent(event);        }      }    }  }  public abstract void onEvent(T event);  public List<K> getChildren() {    return children;  }  }

业务节点

public class MultiSelectNode<T extends MultiSelectNode<T>> extends SimpleTreeNode<T, MultiSelectEvent> {  private boolean isSelected;  // 是否被选中  private boolean isExpand;   // 是否展开  /**   * @param hierarchy view层级 - 用于产生viewType   */  public MultiSelectNode(int hierarchy) {    super(hierarchy);  }  /**   * 事件处理方法   *   * @param event 传递得到的事件   */  @Override  public void onEvent(MultiSelectEvent event) {    switch (event.getNotifyType()) {      case TreeNodeEvent.NOTIFY_CHILDREN:        // 父节点通知子节点改变选择状态        if (event.getEventType() == MultiSelectEvent.EVENT_SET_SELECTED) {          // 如果子节点选择状态有变,则继续通知下层节点改变状态          if (event.isSelected() != isSelected()) {            setSelected(event.isSelected());            notifyChildren(event);          }        }        break;      case TreeNodeEvent.NOTIFY_PARENT:        // 子节点选择状态更改,则通知父节点重新根据所有子节点设置自身状态        if (event.getEventType() == MultiSelectEvent.EVENT_SET_SELECTED) {          if (recheckSelected() != isSelected()) {            setSelected(!isSelected());            // 如果父节点有变,则继续递归通知            notifyParent(event);          }        }        break;      case TreeNodeEvent.NOTIFY_BROTHER:        // 通知兄弟节点改变扩展状态        if (event.getEventType() == MultiSelectEvent.EVENT_SET_EXPAND) {          if (event.isExpand() != isExpand()) {            setExpand(event.isExpand());          }        }        break;      default:        break;    }  }  /**   * 关闭兄弟节点扩展   *   * @param isExpand 是否扩展   */  public void setOtherGroupsExpand(boolean isExpand) {    MultiSelectEvent event = new MultiSelectEvent(MultiSelectEvent.EVENT_SET_EXPAND);    event.setExpand(isExpand);    notifyBrother(event);  }  /**   * 通知父节点根据子节点设置状态   * 注:选择具有递归性,如果父类状态有变会继续通知父类   */  public void setParentRecheckSelected() {    MultiSelectEvent event = new MultiSelectEvent(MultiSelectEvent.EVENT_SET_SELECTED);    notifyParent(event);  }  /**   * 通知子节点设置选择状态   * 注:选择具有递归性,会设置所有孩子以及孩子的孩子状态   *   * @param isSelected 是否选择   */  public void setChildrenSelected(boolean isSelected) {    MultiSelectEvent event = new MultiSelectEvent(MultiSelectEvent.EVENT_SET_SELECTED);    event.setSelected(isSelected);    notifyChildren(event);  }  /**   * 根据子节点设置自身状态   *   * @return isSelected boolean   */  public boolean recheckSelected() {    for (MultiSelectNode child : children) {      if (!child.isSelected()) {        return false;      }    }    return true;  }  public boolean isExpand() {    return isExpand;  }  public void setExpand(boolean expand) {    isExpand = expand;  }  public boolean isSelected() {    return isSelected;  }  public void setSelected(boolean selected) {    isSelected = selected;  }  }

Event事件

public class TreeNodeEvent {  public static final int NOTIFY_PARENT = 1;  public static final int NOTIFY_CHILDREN = 2;  public static final int NOTIFY_BROTHER = 3;  private int notifyType;  public TreeNodeEvent(){  }  public TreeNodeEvent(int notifyType) {    this.notifyType = notifyType;  }  public int getNotifyType() {    return notifyType;  }  public void setNotifyType(int notifyType) {    this.notifyType = notifyType;  }}public class MultiSelectEvent extends TreeNodeEvent {  public static final int EVENT_SET_SELECTED = 1;  public static final int EVENT_SET_EXPAND = 2;  //事件类型  private int eventType;  private boolean isSelected;  private boolean isExpand;}

详细可见Github: https://github.com/zwgg/MultiSelectList

总结

以上所述是小编给大家介绍的Android 绘制多级树形选择列表实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对VEVB武林网网站的支持!


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表