首页 > 系统 > Android > 正文

Android多级树形列表控件

2019-12-12 04:37:42
字体:
来源:转载
供稿:网友

我们开发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源码:http://xiazai.VeVB.COm/201611/yuanma/AndroidTreeView(VeVB.COm).rar

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表