
android多级树形结构列表(完整代码,可以选择某项)
发布日期:2021-05-08 04:55:04
浏览次数:15
分类:原创文章
本文共 13195 字,大约阅读时间需要 43 分钟。
多级列表是个很常见的功能,发现很多代码都不好扩展,或者由于数据结构设计不好,导致开发维护比较费时间。
11月14号更新:增加了 选择按钮,可以实现选中效果。用于选择。
下面自己写了一个,github链接地址: 分享一波。如图所示
看一下节点的代码,最重要的设计TreePoint
public class TreePoint { private String ID; // 7241, //账号id private String NNAME; //名称 private String PARENTID; // 0, //父id 0表示根节点 private String ISLEAF; //0, //是否是叶子节点 1为叶子节点 private int DISPLAY_ORDER; // 1 //同一个级别的显示顺序 private boolean isExpand = false; //是否展开了}
先看下MainActivity
public class MainActivity extends AppCompatActivity { private ReasonAdapter adapter; private ListView listView; private EditText et_filter; private List<TreePoint> reasonPointList = new ArrayList<>(); private HashMap<String, TreePoint> reasonMap = new HashMap<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(); init(); addListener(); } public void setContentView() { setContentView(R.layout.activity_main); } public void init() { adapter = new ReasonAdapter(this, reasonPointList, reasonMap); listView = findViewById(R.id.listView); listView.setAdapter(adapter); et_filter = findViewById(R.id.et_filter); initData(); } //初始化数据 //数据特点:TreePoint 之间的关系特点 id是任意唯一的。 如果为根节点 PARENTID 为"0" 如果没有子节点,也就是本身是叶子节点的时候ISLEAF = "1" // DISPLAY_ORDER 是同一级中 显示的顺序 //如果需要做选中 单选或者多选,只需要给TreePoint增加一个选中的属性,在ReasonAdapter中处理就好了 private void initData() { reasonPointList.clear(); int id =1000; int parentId = 0; int parentId2 = 0; int parentId3 = 0; for(int i=1;i<5;i++){ id++; reasonPointList.add(new TreePoint(""+id,"分类"+i,"" + parentId,"0",i)); for(int j=1;j<5;j++){ if(j==1){ parentId2 = id; } id++; reasonPointList.add(new TreePoint(""+id,"分类"+i+"_"+j,""+parentId2,"0",j)); for(int k=1;k<5;k++){ if(k==1){ parentId3 = id; } id++; reasonPointList.add(new TreePoint(""+id,"分类"+i+"_"+j+"_"+k,""+parentId3,"1",k)); } } } //这里打乱数据的顺序,模拟数据乱序,重新排序 Collections.shuffle(reasonPointList); updateData(); } public void addListener() { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { adapter.onItemClick(position); } }); et_filter.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { searchAdapter(s); } }); } private void searchAdapter(Editable s) { adapter.setKeyword(s.toString()); } //对数据排序 深度优先 private void updateData() { //这里比较重要的是 reasonPointList 这个集合中的内容又使用 reasonMap这个HashMap保存起来,主要是为了利用ArrayList遍历快,HashMap查找快的特征。提高代码的执行效率。 for (TreePoint reasonTreePoint : reasonPointList) { reasonMap.put(reasonTreePoint.getID(), reasonTreePoint); } Collections.sort(reasonPointList, new Comparator<TreePoint>() { @Override public int compare(TreePoint lhs, TreePoint rhs) { int llevel = TreeUtils.getLevel(lhs, reasonMap); int rlevel = TreeUtils.getLevel(rhs, reasonMap); if (llevel == rlevel) { if (lhs.getPARENTID().equals(rhs.getPARENTID())) { //左边小 return lhs.getDISPLAY_ORDER() > rhs.getDISPLAY_ORDER() ? 1 : -1; } else { //如果父辈id不相等 //同一级别,不同父辈 TreePoint lreasonTreePoint = TreeUtils.getTreePoint(lhs.getPARENTID(), reasonMap); TreePoint rreasonTreePoint = TreeUtils.getTreePoint(rhs.getPARENTID(), reasonMap); return compare(lreasonTreePoint, rreasonTreePoint); //父辈 } } else { //不同级别 if (llevel > rlevel) { //左边级别大 左边小 if (lhs.getPARENTID().equals(rhs.getID())) { return 1; } else { TreePoint lreasonTreePoint = TreeUtils.getTreePoint(lhs.getPARENTID(), reasonMap); return compare(lreasonTreePoint, rhs); } } else { //右边级别大 右边小 if (rhs.getPARENTID().equals(lhs.getID())) { return -1; } TreePoint rreasonTreePoint = TreeUtils.getTreePoint(rhs.getPARENTID(), reasonMap); return compare(lhs, rreasonTreePoint); } } } }); adapter.notifyDataSetChanged(); }}
适配器 中通过缩进控制不同的级别
public class ReasonAdapter extends BaseAdapter { private Context mcontext; private Activity mActivity; private List<TreePoint> reasonPointList; private String keyword = ""; private HashMap<String, TreePoint> reasonMap = new HashMap<>(); public ReasonAdapter(final Context mcontext, List<TreePoint> reasonPointList, HashMap<String, TreePoint> reasonMap) { this.mcontext = mcontext; this.mActivity = (Activity) mcontext; this.reasonPointList = reasonPointList; this.reasonMap = reasonMap; } public void setKeyword(String keyword) { this.keyword = keyword; //全部闭合,然后展开 for (TreePoint reasonTreePoint : reasonPointList) { reasonTreePoint.setExpand(false); } if (!"".equals(keyword)) { for (TreePoint reasonTreePoint : reasonPointList) { if (reasonTreePoint.getNNAME().contains(keyword)) { //含有keyword if ("0".equals(reasonTreePoint.getISLEAF())) { reasonTreePoint.setExpand(true); } //打开所有的父级元素 openExpand(reasonTreePoint); } } } this.notifyDataSetChanged(); } private void openExpand(TreePoint reasonTreePoint) { if ("0".equals(reasonTreePoint.getPARENTID())) { reasonTreePoint.setExpand(true); } else { reasonMap.get(reasonTreePoint.getPARENTID()).setExpand(true); openExpand(reasonMap.get(reasonTreePoint.getPARENTID())); } } //第一要准确计算数量 @Override public int getCount() { int count = 0; for (TreePoint tempPoint : reasonPointList) { if ("0".equals(tempPoint.getPARENTID())) { count++; } else { if (getItemIsExpand(tempPoint.getPARENTID())) { count++; } } } return count; } private boolean getItemIsExpand(String ID) { for (TreePoint tempPoint : reasonPointList) { if (ID.equals(tempPoint.getID())) { if (tempPoint.isExpand()) { return true; } else { return false; } } } return false; } @Override public Object getItem(int position) { return reasonPointList.get(convertPostion(position)); } private int convertPostion(int position) { int count = 0; for (int i = 0; i < reasonPointList.size(); i++) { TreePoint treePoint = reasonPointList.get(i); if ("0".equals(treePoint.getPARENTID())) { count++; } else { if (getItemIsExpand(treePoint.getPARENTID())) { count++; } } if (position == (count - 1)) { return i; } } return 0; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(mcontext).inflate(R.layout.adapter_treeview, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.icon = (ImageView) convertView.findViewById(R.id.icon); holder.contactitemBtn = (ImageButton) convertView.findViewById(R.id.contactitem_sendmsg); holder.contactitemBtn.setVisibility(View.GONE); convertView.setTag(holder); convertView.setBackgroundColor(0xffffffff); convertView.setPadding(DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10), DensityUtil.dip2px(mcontext, 10)); } else { holder = (ViewHolder) convertView.getTag(); } TreePoint tempPoint = (TreePoint) getItem(position); int level = TreeUtils.getLevel(tempPoint, reasonMap); holder.icon.setPadding(25 * level, holder.icon.getPaddingTop(), 0, holder.icon.getPaddingBottom()); if ("0".equals(tempPoint.getISLEAF())) { if (tempPoint.isExpand() == false) { holder.icon.setVisibility(View.VISIBLE); holder.icon.setImageResource(R.drawable.outline_list_collapse); } else { holder.icon.setVisibility(View.VISIBLE); holder.icon.setImageResource(R.drawable.outline_list_expand); } } else { holder.icon.setVisibility(View.INVISIBLE); holder.icon.setImageResource(R.drawable.outline_list_collapse); } if (keyword != null && !"".equals(keyword) && tempPoint.getNNAME().contains(keyword)) { int index = tempPoint.getNNAME().indexOf(keyword); int len = keyword.length(); Spanned temp = Html.fromHtml(tempPoint.getNNAME().substring(0, index) + "<font color=#FF0000>" + tempPoint.getNNAME().substring(index, index + len) + "</font>" + tempPoint.getNNAME().substring(index + len, tempPoint.getNNAME().length())); holder.text.setText(temp); } else { holder.text.setText(tempPoint.getNNAME()); } holder.text.setCompoundDrawablePadding(DensityUtil.dip2px(mcontext, 10)); holder.contactitemBtn.setSelected(false); if (tempPoint.isExpand()) holder.contactitemBtn.setSelected(true); return convertView; } public void onItemClick(int position) { TreePoint reasonTreePoint = (TreePoint) getItem(position); if ("1".equals(reasonTreePoint.getISLEAF())) { //处理回填 Toast.makeText(mcontext, getSubmitResult(reasonTreePoint), Toast.LENGTH_SHORT).show(); } else { //如果点击的是父类 if (reasonTreePoint.isExpand()) { for (TreePoint tempPoint : reasonPointList) { if (tempPoint.getPARENTID().equals(reasonTreePoint.getID())) { if ("0".equals(reasonTreePoint.getISLEAF())) { tempPoint.setExpand(false); } } } reasonTreePoint.setExpand(false); } else { reasonTreePoint.setExpand(true); } } this.notifyDataSetChanged(); } private String getSubmitResult(TreePoint reasonTreePoint) { StringBuilder sb = new StringBuilder(); addResult(reasonTreePoint, sb); String result = sb.toString(); if (result.endsWith("-")) { result = result.substring(0, result.length() - 1); } return result; } private void addResult(TreePoint reasonTreePoint, StringBuilder sb) { if (reasonTreePoint != null && sb != null) { sb.insert(0, reasonTreePoint.getNNAME() + "-"); if (!"0".equals(reasonTreePoint.getPARENTID())) { addResult(reasonMap.get(reasonTreePoint.getPARENTID()), sb); } } } class ViewHolder { TextView text; ImageView icon; ImageButton contactitemBtn; //这是个选择的按钮,可以自己适当修改代码实现功能 }}
工具类 获取层级,其实这里可以将TreePoint增加一个层级属性,如果层级属性为空,就重新计算,如果不为空,就直接使用,避免滚动listView的时候多次计算。public class TreeUtils { //第一级别为0 public static int getLevel(TreePoint treePoint,HashMap<String,TreePoint> map){ if("0".equals(treePoint.getPARENTID())){ return 0; }else{ return 1+getLevel(getTreePoint(treePoint.getPARENTID(),map),map); } } public static TreePoint getTreePoint(String ID, HashMap<String,TreePoint> map){ if(map.containsKey(ID)){ return map.get(ID); } Log.e("xlc","ID:" + ID); return null; }}
以上大致就是所有的代码了。主要是为了实现多级列表。方便扩展。要保证效率。
发表评论
最新留言
哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月08日 06时04分25秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
ArduPilot+mavros+gazebo+QGC联合仿真初体验
2021-05-08
px4调试bug--添加mavlink_log_info信息
2021-05-08
redis替换字符串命令
2021-05-08
redis向数组中添加值并查看数组长度
2021-05-08
python3基础梳理11python中模块和包
2021-05-08
JS编写一个函数,计算三个不同数字的大小,按从小到大顺序打印(穷举法)
2021-05-08
jQuery实现轮播图效果
2021-05-08
mybatis中like的注意
2021-05-08
sqlplus的基本使用
2021-05-08
oracle删除表重复数据
2021-05-08
Oracle删除主表数据
2021-05-08
js中两种定时器,setTimeout和setInterval实现验证码发送
2021-05-08
Oracle常用SQL
2021-05-08
技术美术面试问题整理
2021-05-08
Redis分布式锁原理
2021-05-08
【备份】求极限笔记
2021-05-08
C++学习记录 四、基于多态的企业职工系统
2021-05-08
C++学习记录 五、C++提高编程(2)
2021-05-08
面试问道nginx优化怎么做的
2021-05-08