RecyclerView 分层级展示(抽屉) TreeView

news2024/12/28 5:18:01

先看效果

难点:  数据层级的划分;理清楚层级关系, 剩下的就简单明了了;

1. 第一步 new Adapter, and setAdapter; (不需要setLayoutManager, Adapter里有set);

mAdapter = new TreeViewAdapter(getContext(),mRecyclerView);
mRecyclerView.setAdapter(mAdapter);

2. 第二步, 划分数据层级;(不好说, 自己看代码理解)

注意点: CommonMenu的构造方法中初始化层级, 是否展开;

CommonMenu 就是菜单, 菜单中也可能包含菜单, 具体是包含还是并列同级, 取决于Level_等级;

0~3都是菜单, LEVEL_ITEM 为具体对象;

Set数据过程 以及需要的实体类 如下:(具体对象的实体类就贴了一个KaKou, 其他的类似, 继承BaseMenu即可)

            //初始化 Adapter需要的数据mBaseList 
            List<BaseMenu> mBaseList = new ArrayList<>();

            for (int i = 0; i < 2; i++) {
                CommonMenu lcMenu = new CommonMenu(BaseMenu.LEVEL_1, false);
                lcMenu.setName("林场" + i);

                List<BaseMenu> lcChildList = new ArrayList<>();
                lcMenu.setChild(lcChildList);

                CommonMenu yunTaiMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
                yunTaiMenu.setName("云台");
                lcChildList.add(yunTaiMenu);

                List<BaseMenu> ytChildList = new ArrayList<>();
                for (int j = 0; j < 2; j++) {
                    YunTai item = new YunTai();
                    item.setName("云台Name" + j);
                    ytChildList.add(item);
                }
                yunTaiMenu.setChild(ytChildList);

                CommonMenu kkMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
                kkMenu.setName("卡口");
                lcChildList.add(kkMenu);

                List<BaseMenu> kkChildList = new ArrayList<>();
                for (int j = 0; j < 2; j++) {
                    KaKou item = new KaKou();
                    item.setName("卡口Name" + j);
                    kkChildList.add(item);
                }
                kkMenu.setChild(kkChildList);

                CommonMenu fdMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
                fdMenu.setName("防盗");
                lcChildList.add(fdMenu);

                List<BaseMenu> fdChildList = new ArrayList<>();
                for (int j = 0; j < 2; j++) {
                    FangDao item = new FangDao();
                    item.setName("防盗Name" + j);
                    fdChildList.add(item);
                }
                fdMenu.setChild(fdChildList);

                CommonMenu qxzMenu = new CommonMenu(BaseMenu.LEVEL_2, false);
                qxzMenu.setName("气象站");
                lcChildList.add(qxzMenu);

                List<BaseMenu> qxzChildList = new ArrayList<>();
                for (int j = 0; j < 2; j++) {
                    QiXiangZhan item = new QiXiangZhan();
                    item.setName("气象站Name" + j);
                    qxzChildList.add(item);
                }
                qxzMenu.setChild(qxzChildList);


                mBaseList.add(lcMenu);
            }

            mAdapter.setBaseList(mBaseList);
            mAdapter.notifyDataSetChanged();

public abstract class BaseMenu {

    public static final int LEVEL_0 = 0;//林局
    public static final int LEVEL_1 = 1;//林场
    public static final int LEVEL_2 = 2;//云台
    public static final int LEVEL_3 = 3;//备用-管护站
    public static final int LEVEL_ITEM = 4;//具体设备

    protected String name;
    protected int level = LEVEL_ITEM;
    protected boolean expand = true;

    public boolean isExpand() {
        return expand;
    }

    public void setExpand(boolean expand) {
        this.expand = expand;
    }

    public int getLevel() {
        return level;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isItem(){
        return level == LEVEL_ITEM;
    }

    public abstract List<BaseMenu> getChild();

}
public class CommonMenu extends BaseMenu {

    private List<BaseMenu> child;

    public List<BaseMenu> getChild() {
        return child;
    }

    public void setChild(List<BaseMenu> child) {
        this.child = child;
    }

    public CommonMenu(int level, boolean expand) {
        this.level = level;
        this.expand = expand;
    }
}
public class KaKou extends BaseMenu{

    private String uuid;
    private String name;
    private String lon;
    private String lat;

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public List<BaseMenu> getChild() {
        return null;
    }

    public String getLon() {
        return lon;
    }

    public void setLon(String lon) {
        this.lon = lon;
    }

    public String getLat() {
        return lat;
    }

    public void setLat(String lat) {
        this.lat = lat;
    }
}

3. 第三步 愉快的复制粘贴过程(Adapter, 以及 布局)


import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.sxwy.fanghuo.R;
import com.sxwy.fanghuo.data.BaseMenu;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
 * Created by ZhouWengong on 2023/5/17.
 */
public class TreeViewAdapter extends RecyclerView.Adapter<ViewHolder> {
    public static final String TAG = TreeViewAdapter.class.getSimpleName();
    private LinearLayoutManager layoutManager;

    private final List<BaseMenu> mCurrentList;//当前展示的数据
    private Context mContext;

    private RecyclerView mRecyclerView;

    public TreeViewAdapter(Context context, RecyclerView recyclerView) {
        mContext = context;
        mCurrentList = new ArrayList<>();
        if (recyclerView != null) {
            mRecyclerView = recyclerView;
            layoutManager = new LinearLayoutManager(context);
            mRecyclerView.setLayoutManager(layoutManager);

            //展开与关闭时的动画耗时;
            RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
            if (itemAnimator != null) {
                int duration = 10;
                itemAnimator.setAddDuration(duration);
                itemAnimator.setRemoveDuration(duration);
            }
        }
    }

    public void setBaseList(List<BaseMenu> baseList) {
        if (baseList != null) {
            initCurrentMenu(baseList);
        }
    }

    private void initCurrentMenu(List<BaseMenu> baseMenus) {
        for (BaseMenu baseMenu : baseMenus) {
            mCurrentList.add(baseMenu);
            if (!baseMenu.isItem()) {
                if (baseMenu.getChild() != null && baseMenu.isExpand()) {
                    initCurrentMenu(baseMenu.getChild());
                }
            }
        }
    }

    private void onMenuExpand(BaseMenu baseMenu, int position) {
        if (baseMenu.getChild() != null) {
            int size = mCurrentList.size();

            if (baseMenu.isExpand()) {
                int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition();

                insertMenuChild(baseMenu, position + 1);

                int itemCount = mCurrentList.size() - size;
                notifyItemRangeInserted(position + 1, itemCount);
                notifyItemRangeChanged(position, mCurrentList.size());

                int toPosition = position + itemCount;


                if (lastVisibleItemPosition < toPosition) {
                    mRecyclerView.scrollToPosition(toPosition);
                }
            } else {
                removeMenuChild(baseMenu);
                notifyItemRangeRemoved(position + 1, size - mCurrentList.size());
                notifyItemRangeChanged(position, mCurrentList.size());
            }
        } else {
            notifyItemChanged(position);
        }
    }

    private void insertMenuChild(BaseMenu menu, int position) {
        if (menu.isExpand()) {
            List<BaseMenu> child = menu.getChild();
            if (child != null) {
//                for (BaseMenu baseMenu : child) {//child逆序添加,因为position固定;
//                    mCurrentList.add(position , baseMenu);
//                    if (!baseMenu.isItem() && baseMenu.isExpand()) {
//                        insertMenuChild(baseMenu, position + 1);
//                    }
//                }
                for (int i = child.size() - 1; i >= 0; i--) {//逆序遍历, 正序添加
                    BaseMenu baseMenu = child.get(i);
                    mCurrentList.add(position, baseMenu);
                    if (!baseMenu.isItem() && baseMenu.isExpand()) {
                        insertMenuChild(baseMenu, position + 1);
                    }
                }

            }
        }
    }

    private void removeMenuChild(BaseMenu menu) {
        List<BaseMenu> child = menu.getChild();
        if (child != null) {
            mCurrentList.removeAll(child);
            for (BaseMenu baseMenu : child) {
                removeMenuChild(baseMenu);
            }
        }
    }


    public int getLayoutId(int viewType) {
        int ret = 0;
        switch (viewType) {
            case BaseMenu.LEVEL_0:
                break;
            case BaseMenu.LEVEL_1:
                ret = R.layout.item_treeview_menu_1;
                break;
            case BaseMenu.LEVEL_2:
                ret = R.layout.item_treeview_menu_2;
                break;
            case BaseMenu.LEVEL_3:
                break;
            case BaseMenu.LEVEL_ITEM:
                ret = R.layout.item_treeview_menu_item;
                break;
        }

        return ret;
    }

    @Override
    public int getItemViewType(int position) {
        return mCurrentList.get(position).getLevel();
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return ViewHolder.getViewHolder(mContext, parent, getLayoutId(viewType));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        BaseMenu baseMenu = mCurrentList.get(position);
        ((TextView) holder.getView(R.id.text)).setText(baseMenu.getName());
        if (!baseMenu.isItem()) {//我的图片资源箭头是朝右的,根据实际情况修改即可
            holder.getView(R.id.indicator).setRotation(baseMenu.isExpand() ? 90 : 270);
        }
        View holderView = holder.getView(R.id.parent);
        holderView.setTag(position);
        holderView.setOnClickListener(onClickListener);

    }

    private final View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int position = (int) v.getTag();
            BaseMenu baseMenu = mCurrentList.get(position);
            if (!baseMenu.isItem()) {
                baseMenu.setExpand(!baseMenu.isExpand());
                onMenuExpand(baseMenu, position);
            }else {
                //todo item点击时触发
            }
        }
    };

    @Override
    public int getItemCount() {
        return mCurrentList.size();
    }

}

布局只传了一个, 根据情况复制粘贴就可以,

注意: 每个Level 对应一个布局, 每个布局的paddingStart应该设置合理的间距(@dimen/treeViewMarginLevel_1)

item_treeview_menu_1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parent"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@drawable/shape_white_radius_5"
        android:gravity="center_vertical"
        android:paddingStart="@dimen/treeViewMarginLevel_1">

        <ImageView
            android:id="@+id/headPic"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:src="@mipmap/ic_cf_icon" />


        <TextView
            android:id="@+id/text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="名字"
            android:textColor="@color/text_black"
            android:textSize="14sp"
            android:textStyle="bold" />


        <ImageView
            android:id="@+id/indicator"
            android:layout_width="15dp"
            android:layout_height="15dp"
            android:layout_marginStart="20dp"
            android:layout_marginEnd="10dp"
            android:rotation="90"
            android:src="@mipmap/ic_next_page_indicator"
            android:visibility="visible" />
    </LinearLayout>

    <View
        android:layout_width="0dp"
        android:layout_height="1dp" />


</LinearLayout>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/536874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

素雅的登录界面,简单而优雅

先上效果图&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>*, *::after, *::before {margin: 0;padding: 0;box-sizing: bord…

本地服务器与云服务器哪个好?

本地服务器和云服务器是企业可以使用的两种不同的服务器设置。主要区别在于本地服务器托管&#xff0c;第三方提供商托管云服务器。那么&#xff0c;本地服务器和云服务器哪个更好呢&#xff1f; 接下来&#xff0c;将带大家讨论本地服务器和云服务器的优缺点&#xff0c;并帮…

Golang笔记:使用embed包将静态资源嵌入到程序中

文章目录 目的使用演示//go:embed 指令在WebServer中应用总结 目的 Golang编译程序默认是静态编译&#xff0c;会把相关的库都打包到一起&#xff0c;这在分发部署使用时非常方便。不过如果项目中用到的外部的静态资源文件&#xff0c;通常就需要将这些资源和程序一起拷贝分发…

软件测试面试大全(涵盖了软件测试的全部核心技术点),应对技术面妥妥的

软件测试面试题&#xff1a;项目 1、简单介绍下最近做过的项目 根据自己的项目整理完成&#xff0c;要点&#xff1a; 1&#xff09;项目背景、业务、需求、核心业务的流程 2&#xff09;项目架构&#xff0c;B/S 还是 C/5&#xff0c;数据库用的什么? 中间件用的什么&…

[开源工具] [Unity实战]Jenkins如何配置拉取Git/Jenkins使用Unity一键打包[windows][android]

[开源工具]Jenkins如何配置拉取Git/Jenkins使用Unity一键打包[windows][android] 1.背景介绍1.1Jenkins是什么?1.2用JenkinsUnity的好处? 2.Jenkins安装&使用2.1Java112.2 下载jenkins.war(我用tomcat所以需要,可以用jenkins安装版本)2.3 使用Tomcat92.4将tomcat9解压后,…

1.高级面试-MySQL、Redis、特殊场景、Java

本文目录如下&#xff1a; 高级面试一、MySQLB树有什么优点&#xff1f; image.pngInnoDB 和 MyISAM 的索引结构有什么区别 (聚簇索引-非聚簇索引)&#xff1f; 二、RedisRedis 如何保证存储的都是 热点数据&#xff1f;大量 key 为同一过期时间怎么办&#xff1f;缓存一致性的…

TS构建微信小程序后使用vant weapp框架配置

1.npm 安装 # 通过 npm 安装 npm i vant/weapp -S --production 2.配置app.json 将 app.json 中的 "style": "v2" 去除&#xff0c;小程序的新版基础组件强行加上了许多样式&#xff0c;难以覆盖&#xff0c;不关闭将造成部分组件样式混乱。 3.修改 pr…

ubuntu20 kvm显卡直通实验-a4000

一、环境&#xff1a; 显卡&#xff1a;NVIDIA RTX A4000 系统&#xff1a;Ubuntu20.04 CPU&#xff1a;intel二、安装kvm sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager ovmf添加用户到“libvirt”和“kvm”组 s…

个人简历上的照片有什么要求?这些基本要求要知道

简历对我们来说非常重要&#xff0c;因为它可以帮助我们展示我们的工作经验、技能和教育背景&#xff0c;从而让面试者更好地了解我们的能力和合适程度。简历通常是我们与公司之间的第一次接触&#xff0c;因此它应该清晰、简洁和易于阅读&#xff0c;以便引起面试者的兴趣&…

【机械硬盘重新挂载】

一、背景描述 目前机器上有一个6.5T的机械硬盘&#xff0c;挂在为/home目录&#xff0c;但是目前处于公司业务需要&#xff0c;需要将机械硬盘重新挂载为/data&#xff0c;但是/home与/data中都有业务数据&#xff0c;需要我们谨慎操作。 二、概念解析 挂载源&#xff1a;要…

机器学习项目实战-能源利用率 Part-1(数据清洗)

1. 项目背景 2009年的《当地法案84号》&#xff0c;或纽约市基准法案&#xff0c;要求对能源和用水量进行年度基准测试和披露信息。被覆盖的财产包括单个建筑物的税收地块&#xff0c;其总建筑面积大于50,000平方英尺&#xff08;平方英尺&#xff09;&#xff0c;以及具有超过…

CISP-PTE认证是什么?含金量有多高?

CISP即“注册信息安全专业人员”&#xff0c;由中国信息安全测评中心根据中央编办授权&#xff0c;系国家对信息安全人员资质的最高认可。那么&#xff0c;CISP-PTE认证又是什么呢&#xff1f;它的含金量又有多高&#xff1f;下面&#xff0c;国科科技就为你介绍。 CISP-PTE是…

什么是Redisson分布式锁?有什么作用?

前言&#xff1a; 如果你的简历中写了做过电商项目&#xff0c;那么面试官基本都会从SpringBoot、SpringCloud以及Dubbo这些微服务架构涉及的知识问起&#xff0c;然后深入到问什么是分布事务、分布式锁以及分布式缓存等内容。 这篇文章主要聊聊什么是Redisson分布式锁&#…

【Java EE 初阶】线程安全及死锁解决方案

目录 1.多线程下线程不安全的问题 1.使用多个线程对Array List集合进行添加操作并打印&#xff0c;查看结果 2.如何在多线程环境下使用线程安全的集合类 CopyOnWriteArrayList 3.多线程环境下使用队列 4.多线程环境下使用哈希表 1.HashTable线程安全 2.Concurrent Hash M…

yolov8训练记录

resume: 将model设置为最近一次训练的权重路径 (last.pt) resume设置为True YOLOv8 在 Python 环境中直接使用&#xff0c;不用 ultralytics 库 pip uninstall ultralytics 原因&#xff1a;安装 ultralytics 库&#xff0c;只能在虚拟环境中使用&#xff0c;自己修改代码非常…

ACM 1004 | 母牛的故事

文章目录 0x00 前言 0x01 题目描述 0x02 问题分析 0x03 代码设计 0x04 完整代码 0x05 运行效果 0x06 总结 0x00 前言 C 语言网不仅提供 C 语言&#xff0c;还包括 C 、 java 、算法与数据结构等课程在内的各种入门教程、视频录像、编程经验、编译器教程及软件下载、题解博…

新库上线 | CnOpenData·A股上市公司担保数据

A股上市公司担保数据 一、数据简介 “对外担保”&#xff0c;是指上市公司为他人提供的担保&#xff0c;包括上市公司对控股子公司的担保。“上市公司及其控股子公司的对外担保总额”&#xff0c;是指包括上市公司对控股子公司担保在内的上市公司对外担保总额与上市公司控股子…

初级数据结构——栈和队列

目录 1.栈栈的概念及结构栈的实现栈的结构初始化栈入栈出栈获取栈顶元素获取栈中有效元素个数检测栈是否为空销毁栈Stack.hStack.cTest.c 2.队列队列的概念及结构队列的实现队列的结构初始化队列队尾入队列队头出队列获取队列头部元素获取队列队尾元素获取队列中有效元素个数检…

【FATE联邦学习】model not init, call init_model() function

太长不看版 在local_mode下&#xff0c;不要使用t.CustModel进行实例化&#xff0c;而是直接使用原本的类进行实例化。 如果你设置了trainer.local_mode()&#xff0c;那么trainer里面的model不可以是t.CustModel()的实例。 给几个example&#xff1a; 正确&#xff1a; mod…

2023年美国大学生数学建模竞赛B题重塑马赛马拉的形象解题全过程文档及程序

2023年美国大学生数学建模竞赛 B题 重塑马赛马拉的形象 原题再现&#xff1a; 背景介绍   肯尼亚的野生动物保护区最初主要是为了保护野生动物和其他自然资源而设立的。肯尼亚议会通过了2013年《野生动物保护和管理法》&#xff0c; 以提供更公平的资源共享&#xff0c;以及…