【Android】二级分类双列表联动Demo

先上图:
在这里插入图片描述

Demo解释

demo使用的是双列表展示(准确的说是三个,二级分类那里嵌套了一个),点击左边的条目,右边的列表会跳转相应的条目,滑动右边的列表,左边的列表也会相应的滑动。

代码

主页面布局

这个很简单,就是两个RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/one"
        android:layout_weight="7"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/two"
        android:layout_weight="3"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

适配器

因为有三个列表RecyclerView,所以有三个适配器:
OneTypeAdapter

public class OneTypeAdapter extends RecyclerView.Adapter<OneTypeAdapter.OneTypeHolder> {
    private final Context context;
    private final List<String> list;
    public int selectedPosition = 0;//当前选择的下标
    private OnItemClickListener onItemClickListener;

    public OneTypeAdapter(Context context, List<String> list) {
        this.context = context;
        this.list = list;
    }

    @NonNull
    @Override
    public OneTypeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_main_left,parent,false);
        return new OneTypeHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull OneTypeHolder holder, @SuppressLint("RecyclerView") int position) {

        holder.type.setText(list.get(position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (onItemClickListener!=null){
                    onItemClickListener.onItemClickListener(v,position);
                }
            }
        });
        if (position==selectedPosition){
            holder.itemView.setBackgroundColor(context.getColor(R.color.white));
            holder.type.setTextColor(context.getColor(R.color.colorAccent));
            holder.type.setTextSize(32);
        }
        else {
            holder.itemView.setBackgroundColor(context.getColor(R.color.flow));
            holder.type.setTextColor(context.getColor(R.color.black));
            holder.type.setTextSize(24);
        }
    }

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

    public static class OneTypeHolder extends RecyclerView.ViewHolder{
        TextView type;
        public OneTypeHolder(@NonNull View itemView) {
            super(itemView);
            type = itemView.findViewById(R.id.type);
        }
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public interface OnItemClickListener {
        void onItemClickListener(View v, int position);
    }
}

TwoTypeAdapter

public class TwoTypeAdapter extends RecyclerView.Adapter<TwoTypeAdapter.TwoTypeHolder> {
    private Context context;
    private List<String> name;
    private List<String> detail;

    public TwoTypeAdapter(Context context, List<String> name, List<String> detail) {
        this.context = context;
        this.name = name;
        this.detail = detail;
    }

    @NonNull
    @Override
    public TwoTypeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_main_right,parent,false);
        return new TwoTypeHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull TwoTypeHolder holder, int position) {
        holder.type.setText(name.get(position));
        holder.more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "更多", Toast.LENGTH_SHORT).show();
            }
        });
        holder.recyclerView.setAdapter(new DetailAdapter(context, detail));
        holder.recyclerView.setLayoutManager(new GridLayoutManager(context,3));
    }

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

    public static class TwoTypeHolder extends RecyclerView.ViewHolder{
        TextView type,more;
        RecyclerView recyclerView;
        public TwoTypeHolder(@NonNull View itemView) {
            super(itemView);
            type = itemView.findViewById(R.id.type);
            more = itemView.findViewById(R.id.more);
            recyclerView = itemView.findViewById(R.id.right_recycle);
        }
    }
}

DetailAdapter

public class DetailAdapter extends RecyclerView.Adapter<DetailAdapter.ThreeTYpeHolder> {
    private final Context context;
    private final List<String> list;

    public DetailAdapter(Context context, List<String> list) {
        this.context = context;
        this.list = list;
    }

    @NonNull
    @Override
    public ThreeTYpeHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_main_details,parent,false);
        return new ThreeTYpeHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ThreeTYpeHolder holder, int position) {
        holder.type.setText(list.get(position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, list.get(position), Toast.LENGTH_SHORT).show();
            }
        });
    }

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

    public static class ThreeTYpeHolder extends RecyclerView.ViewHolder{
        TextView type;
        ImageView icon;
        public ThreeTYpeHolder(@NonNull View itemView) {
            super(itemView);
            type = itemView.findViewById(R.id.type);
            icon = itemView.findViewById(R.id.icon);
        }
    }
}

条目布局

item_main_left

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_main_left_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="horizontal"
    android:padding="5dp">
    <TextView
        android:id="@+id/type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:layout_marginVertical="15dp" />
</LinearLayout>

item_main_right

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fff"
    android:padding="10dp">

    <TextView
        android:id="@+id/type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#333"
        android:textSize="18sp" />
    <TextView
        android:id="@+id/more"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查看更多 >"
        android:layout_alignParentEnd="true"
        android:textColor="#999"
        android:textSize="14sp" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/right_recycle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/type"/>

</RelativeLayout>

item_main_details

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

    <ImageView
        android:id="@+id/icon"
        android:layout_width="70dp"
        android:layout_height="70dp" />

    <TextView
        android:id="@+id/type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp" />
</LinearLayout>

主页面代码

左边条目点击事件回调

oneTypeAdapter.setOnItemClickListener(new OneTypeAdapter.OnItemClickListener() {
            @Override
            public void onItemClickListener(View v, int position) {
                LinearLayoutManager twoRecyclerLayoutManager = ((LinearLayoutManager) twoRecycler.getLayoutManager());
                if (twoRecyclerLayoutManager!=null){
                    /**
                     这里有个问题,可能因为字号等问题,左边最后n个条目可能出现点击了但是颜色没渲染上的问题,不知道啥原因
                     然后我就是最后n个点击事件手动处理,比如我这就是最后3个点击有问题
                     */
                    if (position>= oneList.size()-3){
                        oneTypeAdapter.selectedPosition=position;
                        oneTypeAdapter.notifyDataSetChanged();
                        twoRecyclerLayoutManager.scrollToPositionWithOffset(position,0);
                    }
                    else {
                        twoRecyclerLayoutManager.scrollToPositionWithOffset(position,0);
                    }
                }
            }
        });

右边列表滑动监听

twoRecycler.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                LinearLayoutManager twoRecyclerLayoutManager = (LinearLayoutManager) twoRecycler.getLayoutManager();
                LinearLayoutManager oneRecyclerLayoutManager = ((LinearLayoutManager) oneRecycler.getLayoutManager());

                if (twoRecyclerLayoutManager!=null&&oneRecyclerLayoutManager!=null){
                    /**
                     获取第一个可见的item的position
                     */
                    currentPosition = twoRecyclerLayoutManager.findFirstVisibleItemPosition();
                    Log.e("TAG", "onScrollChange: "+currentPosition );
                    /**
                     这地方需要进行判断,如果右边的Recycle在移动的时候,左边的RecyclerView也是需要进行移动的
                     左边的recyclerview有可能会不可见,这时候,我们必须去判断一下,左边最后的一个item是不是
                     小于右边滑动的位置,或左边第一个item是不是大于右边滑动的位置
                     */
                    if (oneRecyclerLayoutManager.findFirstVisibleItemPosition() > currentPosition) {
                        oneRecyclerLayoutManager.scrollToPositionWithOffset(currentPosition, 0);
                    } else if (oneRecyclerLayoutManager.findFirstVisibleItemPosition() < currentPosition) {
                        oneRecyclerLayoutManager.scrollToPositionWithOffset(currentPosition, 0);
                    }

                    /**
                     判断右边是否滑动到最后一个item,是的话,也将左边移动到最后一个item
                     canScrollVertically(1)表示是否能向上滚动,false表示已经滚动到底部
                     */
                    if (!twoRecycler.canScrollVertically(1)) {
                        currentPosition = oneList.size() - 1;
                    }
                    oneTypeAdapter.selectedPosition=currentPosition;
                    oneTypeAdapter.notifyDataSetChanged();
                }
            }
        });

完整代码

public class MainActivity extends AppCompatActivity {
    private RecyclerView oneRecycler;
    private RecyclerView twoRecycler;

    private OneTypeAdapter oneTypeAdapter;
    private TwoTypeAdapter twoTypeAdapter;
    private List<String> oneList = new ArrayList<>();

    private List<String> twoList = new ArrayList<>();
    private int currentPosition = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initData();

        oneRecycler = findViewById(R.id.one);
        twoRecycler = findViewById(R.id.two);
        oneRecycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        twoRecycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

        oneTypeAdapter=new OneTypeAdapter(this, oneList);
        twoTypeAdapter=new TwoTypeAdapter(this, oneList,twoList);

        oneRecycler.setAdapter(oneTypeAdapter);
        twoRecycler.setAdapter(twoTypeAdapter);

        oneTypeAdapter.setOnItemClickListener(new OneTypeAdapter.OnItemClickListener() {
            @Override
            public void onItemClickListener(View v, int position) {
                LinearLayoutManager twoRecyclerLayoutManager = ((LinearLayoutManager) twoRecycler.getLayoutManager());
                if (twoRecyclerLayoutManager!=null){
                    /**
                     这里有个问题,可能因为字号等问题,左边最后n个条目可能出现点击了但是颜色没渲染上的问题,不知道啥原因
                     然后我就是最后n个点击事件手动处理,比如我这就是最后3个点击有问题
                     */
                    if (position>= oneList.size()-3){
                        oneTypeAdapter.selectedPosition=position;
                        oneTypeAdapter.notifyDataSetChanged();
                        twoRecyclerLayoutManager.scrollToPositionWithOffset(position,0);
                    }
                    else {
                        twoRecyclerLayoutManager.scrollToPositionWithOffset(position,0);
                    }
                }
            }
        });


        twoRecycler.setOnScrollChangeListener(new View.OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                LinearLayoutManager twoRecyclerLayoutManager = (LinearLayoutManager) twoRecycler.getLayoutManager();
                LinearLayoutManager oneRecyclerLayoutManager = ((LinearLayoutManager) oneRecycler.getLayoutManager());

                if (twoRecyclerLayoutManager!=null&&oneRecyclerLayoutManager!=null){
                    /**
                     获取第一个可见的item的position
                     */
                    currentPosition = twoRecyclerLayoutManager.findFirstVisibleItemPosition();
                    Log.e("TAG", "onScrollChange: "+currentPosition );
                    /**
                     这地方需要进行判断,如果右边的Recycle在移动的时候,左边的RecyclerView也是需要进行移动的
                     左边的recyclerview有可能会不可见,这时候,我们必须去判断一下,左边最后的一个item是不是
                     小于右边滑动的位置,或左边第一个item是不是大于右边滑动的位置
                     */
                    if (oneRecyclerLayoutManager.findFirstVisibleItemPosition() > currentPosition) {
                        oneRecyclerLayoutManager.scrollToPositionWithOffset(currentPosition, 0);
                    } else if (oneRecyclerLayoutManager.findFirstVisibleItemPosition() < currentPosition) {
                        oneRecyclerLayoutManager.scrollToPositionWithOffset(currentPosition, 0);
                    }

                    /**
                     判断右边是否滑动到最后一个item,是的话,也将左边移动到最后一个item
                     canScrollVertically(1)表示是否能向上滚动,false表示已经滚动到底部
                     */
                    if (!twoRecycler.canScrollVertically(1)) {
                        currentPosition = oneList.size() - 1;
                    }
                    oneTypeAdapter.selectedPosition=currentPosition;
                    oneTypeAdapter.notifyDataSetChanged();
                }
            }
        });
    }

    private void initData() {
        for (int i = 0; i < 20; i++) {
            oneList.add("一级类型"+i);
        }
        for (int i=0;i<10;i++){
            twoList.add("二级类型"+i);
        }
    }
}

源码地址:
github:Categorydemo
gitee:Categorydemo

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024 年 10 款最佳免费无限的数据恢复软件工具

十大无限的数据恢复软件工具 数据丢失可能是一场噩梦&#xff0c;无论是由于意外删除、系统崩溃还是硬件故障。值得庆幸的是&#xff0c;有多种数据恢复软件工具可以帮助您检索珍贵的文件和文档。在本文中&#xff0c;我们将探讨可以拯救世界的十大最佳免费无限数据恢复软件工…

第二百零五回

文章目录 1. 概念介绍2. 实现方法2.1 文字信息2.2 红色边框 3. 示例代码4. 内容总结 我们在上一章回中介绍了"如何实现密码输入框"相关的内容&#xff0c;本章回中将介绍如何在在输入框中提示错误.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们…

RT-Thread线程管理(使用篇)

layout: post title: “RT-Thread线程管理” date: 2024-1-26 15:39:08 0800 tags: RT-Thread 线程管理(使用篇) 之后会做源码分析 线程是任务的载体&#xff0c;是RTT中最基本的调度单位。 线程执行时的运行环境称为上下文&#xff0c;具体来说就是各个变量和数据&#xff0c…

stable diffusion学习笔记——高清修复

ai画图中通常存在以下痛点&#xff1a; 受限于本地设备的性能&#xff08;主要是显卡显存&#xff09;&#xff0c;无法跑出分辨率较高的图片。生图的时候分辨率一调大就爆显存。即便显存足够。目前主流的模型大多基于SD1.0和SD1.5&#xff0c;这些模型在训练的时候通常使用小…

【Java】IoC五大注解和@Bean注解

1、用于类的五大注解 Controller 控制器存储 Service 服务存储 Component 组件存储 Configuration 配置存储 Repository 仓库存储 从功能上看&#xff0c;除了Controller&#xff0c;其他效果一样&#xff1a;接口的入口必须为Controller ​几者的关系如下 2、用于方法的…

Leetcode92:反转链表II(区间反转链表)

一、题目 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a…

一篇文章解决你的无线AP选型难题:从入门到精通

无线网络覆盖项目中&#xff0c;无线AP的合理选型和部署非常重要。今天给大家安排。 这篇文章&#xff0c;给你总结了6类典型的无线组网场所&#xff0c;针对每种场景的特点&#xff0c;给出相应的设备选型和部署的方案&#xff0c;同时整理了一些部署无线AP过程中容易忽略的细…

阿里云服务器一键部署幻兽帕鲁专属服务器,图形化可视化修改游戏参数是如此简单!

幻兽帕鲁这款游戏最近很火&#xff0c;在线玩家太多了&#xff0c;官方服务器也是承压巨大&#xff0c;很多玩家现在都是搭建自己的专属幻兽帕鲁服务器来玩。搭建教程现在也是越来越简单了&#xff0c;可以说是真正的零基础&#xff0c;一看就会&#xff0c;并且开箱即玩&#…

GMT绘图笔记

(1)图框设置。在利用GMT绘制图件时&#xff0c;需要设置边框的类型&#xff0c;字体的大小&#xff0c;标记距离边框的距离。主要涉及的参数有&#xff1a; gmt set MAP_FRAME_TYPE plain/fancy 可以调整边框为火车轨道或者线段。 (2)调整图框的粗细&#xff1a;主要是包含有…

浅析Redis③:命令处理之数据返回Client(下)

写在前面 Redis作为我们日常工作中最常使用的缓存数据库&#xff0c;其重要性不言而喻&#xff0c;作为普通开发者&#xff0c;我们在日常开发中使用Redis&#xff0c;主要聚焦于Redis的基层数据结构的命令使用&#xff0c;很少会有人对Redis的内部实现机制进行了解&#xff0c…

Python算法题集_合并区间

本文为Python算法题集之一的代码示例 题目56&#xff1a;合并区间 说明&#xff1a;以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需…

leetcode 1.两数之和(C++)DAY1(待补充哈希表法)

文章目录 1.题目描述示例提示 2.解答思路3.实现代码结果4.总结 1.题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&…

假期2.3

第二章 引用内联重载 一&#xff0e;选择题-* 1、适宜采用inline定义函数情况是&#xff08;C&#xff09; A. 函数体含有循环语句 B. 函数体含有递归语句‘、考科一 ’ C. 函数代码少、频繁调用 D. 函数代码多、不常调用 2、假定一个函数为A(int i4, int j0) {;}, 则执行“A …

Datawhale组队学习 Task10 环境影响

第12章 环境影响 在本章中&#xff0c;首先提出一个问题&#xff1a;大语言模型对环境的影响是什么&#xff1f; 这里给出的一个答案是&#xff1a;气候变化 一方面&#xff0c;我们都听说过气候变化的严重影响(文章1、文章2)&#xff1a; 我们已经比工业革命前的水平高出1.…

LeetCode热题HOT100【栈的压入、弹出序列】

&#x1f525;LeetCode热题HOT100【栈的压入、弹出序列】 1. 题目来源2.题目 1. 题目来源 来自LeetCode热题HOT100 https://leetcode.cn/studyplan/top-100-liked/?isDarktrue 2.题目 题目地址 Leetcode地址 3.Stack 在Java中&#xff0c;Stack 是一个基于后进先出&#…

玩美移动为花西子海外官网打造AR虚拟试妆决方案

全球领先的增强现实&#xff08;AR&#xff09;及人工智能&#xff08;AI&#xff09;美妆科技领导者及玩美系列APP开发商——玩美移动&#xff08;纽交所代码&#xff1a;PERF&#xff09;于近日宣布携手知名美妆品牌花西子&#xff0c;在其线海外官方网页提供多项彩妆虚拟试妆…

TanDEM-X30米DEM数据介绍

一、背景 之前介绍了Copernicus 30米DEM以及Alos 30米DEM数据的详细介绍以及接入到Cesium中的效果展示&#xff0c;有遥感专业工作者对比了Copernnicus、ALOA、ASTER、NASA、SRTM这几家30米DEM数据&#xff0c;得出了Copernicus 30米DEM数据是最好的全球级30米DEM数据&#xf…

Java8 中文指南(一)

Java8 中文指南&#xff08;一&#xff09; 文章目录 Java8 中文指南&#xff08;一&#xff09;《Java8 指南》中文翻译接口的默认方法(Default Methods for Interfaces)Lambda 表达式(Lambda expressions)函数式接口(Functional Interfaces)方法和构造函数引用(Method and Co…

Unity 图片不改变比例适配屏幕

Unity 图片不改变比例适配屏幕 前言项目场景布置代码编写添加并设置脚本效果 前言 遇到一个要让图片适应相机大小&#xff0c;填满屏幕&#xff0c;但不改变图片比例的需求&#xff0c;记录一下。 项目 场景布置 代码编写 创建AdaptiveImageBackground脚本 using System.C…

QT 应用中集成 Sentry

QT 应用中集成 Sentry QT应用中集成 SentrySentry SDK for C/C注册 Sentry 账号QT 应用中集成 Sentry触发 Crash 上报 QT应用中集成 Sentry Sentry 是一个开源的错误监控和日志记录平台&#xff0c;旨在帮助开发团队实时捕获、跟踪和解决软件应用程序中的错误和异常。它提供了…