当前位置: 移动技术网 > 移动技术>移动开发>Android > Android多级树形列表控件

Android多级树形列表控件

2019年07月24日  | 移动技术网移动技术  | 我要评论
我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。 所以我们就要自己去实现一个类似treel

我们开发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); 
    } 
  } 
 
} 

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

demo源码:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网