当前位置: 首页 > news >正文

recyclerview嵌套,实现水平方向联合滑动

文章目录

  • 前言
  • 1、效果展示
  • 2、布局
  • 关键代码实现
    • 内层RecyclerView的适配器
    • 自定义RecyclerView
  • 外层RecyclerView的适配器
  • 数据实现
  • 页面布局
    • 主页面(templater_table_fragment.xml)
    • 嵌套页面(templater_table_fragment_table_rv.xml)
    • 嵌套子页面布局(template_table_record_recycler_view.xml)

前言

在Android中没有控件可以直接展示表格,但是有的时候又需要构建类似于表格的布局,通过RecyclerView直接嵌套实现表格,但是需要实现嵌套RecyclerView的子项实现水平方向联动滑动的效果比较麻烦,接下来看一下如何实现。

1、效果展示

表格水平滑动

2、布局

在这里插入图片描述

关键代码实现

内层RecyclerView的适配器

在onCreateViewHolder的方法,
绑定需要的同步的子项SyncHorizontalScrollRecyclerView,
在onBindViewHolder的方法中,初始化子项SyncHorizontalScrollRecyclerView

class TableChildAdapter<T>(val context: Context, val type:Int, val mutableList: T):RecyclerView.Adapter<TableChildAdapter.TableViewHolder>() {

    private val horizontalViews = mutableListOf<SyncHorizontalScrollRecyclerView>()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TableViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.template_table_record_recycler_view,parent,false)
       return TableViewHolder(view).apply {

           sampleRecyclerView.addToSyncGroup(*horizontalViews.toTypedArray())
               horizontalViews.add(sampleRecyclerView)
           }
    }

    override fun getItemCount(): Int {
        return (mutableList as MutableList<*>).size


    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onBindViewHolder(holder: TableViewHolder, position: Int) {

        if (type == RECORD_TYPE_GRID){
            holder.textView.text =  (mutableList as MutableList<*>)[position] as String
            if (position %2 == 1) {
                holder.textView.textSize = context.fontSizeOf(10)
                holder.textView.setTextColor(context.getColor(R.color.purple_700))
            }
        }else{
            holder.textView.visibility = View.GONE

            val innerAdapter = initInnerAdapter(position)

            with(holder.sampleRecyclerView){
                  layoutManager = LinearLayoutManager(context,LinearLayoutManager.HORIZONTAL,false)
                  adapter = innerAdapter
                innerAdapter.submitList((mutableList as MutableList<*>)[position] as List<String>?)
              }

        }


    }

    private fun initInnerAdapter(position: Int): BaseQuickAdapter<String,QuickViewHolder> {
      return  object : BaseQuickAdapter<String, QuickViewHolder>(){
            override fun onBindViewHolder(
                holder: QuickViewHolder,
                subposition: Int,
                item: String?
            ) {
                val textItem = holder.itemView.findViewById<TextView>(R.id.table_sample_key)
                textItem.text = item
                if (position != 0){
                    textItem.setTextColor(context.getColor(R.color.purple_700))
                    textItem.textSize = context.fontSizeOf(10)
                }
            }

            override fun onCreateViewHolder(
                context: Context,
                parent: ViewGroup,
                viewType: Int
            ): QuickViewHolder {

                return QuickViewHolder(LayoutInflater.from(context).inflate(R.layout.template_table_sample_recycler_view,parent,false))

            }

        }
    }


    class TableViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
        var textView: TextView = itemView.findViewById(R.id.table_record_key)
        val sampleRecyclerView:SyncHorizontalScrollRecyclerView = itemView.findViewById(R.id.table_sample_key_rv)

    }

    companion object {
        val RECORD_TYPE_GRID = 0
        val SAMPLE_TYPE_LIN = 1
    }
}


自定义RecyclerView

在SyncHorizontalScrollRecyclerView中添加滑动监听,在OnScrollListener中进行同步滑动

class SyncHorizontalScrollRecyclerView(context: Context, attrs: AttributeSet) : RecyclerView(context, attrs) {
    // 同步滚动组(所有需要联动的RecyclerView)
    private val syncGroup = mutableListOf<SyncHorizontalScrollRecyclerView>()
    private var firstPos = 0
    private var firstOffset = 0

    init {
        overScrollMode = OVER_SCROLL_NEVER // 禁用边缘发光效果
        //性能优化
        this.setHasFixedSize(true)
        val layoutManager = this.layoutManager as LinearLayoutManager?

        // 通过移动layoutManager来实现列表滑动  此行是让新加载的item条目保持跟已经滑动的recycleView位置保持一致
        // 也就是上拉加载更多的时候  保证新加载出来的item 跟已经滑动的item位置保持一致
        if (layoutManager != null && firstPos > 0 && firstOffset > 0) {
            layoutManager.scrollToPositionWithOffset(firstPos + 1, firstOffset)
        }

        //添加当前滑动recycleView的滑动监听
        this.addOnScrollListener(object : OnScrollListener() {
            override fun onScrolled(currentRV: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(currentRV, dx, dy)
                val currentLM = currentRV.layoutManager as LinearLayoutManager
                //获取显示第一个item的位置
                val currentItemPos = currentLM.findFirstVisibleItemPosition()
                val currentItem = currentLM.getChildAt(0)
                if (currentItem != null) {
                    //获取第一个item的偏移量
                    val firstRight = currentLM.getDecoratedRight(currentItem)
                    //遍历其它的所有的recycleView条目
                    syncGroup.forEach { otherRV->
                        if (currentRV != otherRV){
                            val otherLayoutManager = otherRV.layoutManager as LinearLayoutManager
                            firstPos = currentItemPos
                            firstOffset = firstRight

                            //通过当前显示item的位置和偏移量的位置来置顶recycleView 也就是同步其它item的移动距离
                            otherLayoutManager.scrollToPositionWithOffset(  currentItemPos + 1, firstRight)
                        }
                    }

                }
            }
        })

    }

    // 加入同步组
    fun addToSyncGroup(vararg views: SyncHorizontalScrollRecyclerView) {
        syncGroup.addAll(views)
        views.forEach { it.syncGroup.add(this) }
    }

}

关键实现代码:

 otherLayoutManager.scrollToPositionWithOffset(  currentItemPos + 1, firstRight)

外层RecyclerView的适配器

添加依赖

implementation "io.github.cymchad:BaseRecyclerViewAdapterHelper:4.0.0-beta14"

BaseQuickAdapter继承于RecyclerView.Adapter,将一些多余的方法进行封装

class TableParentAdapter:BaseQuickAdapter<TableTempBean,QuickViewHolder>() {
    override fun onBindViewHolder(holder: QuickViewHolder, position: Int, item: TableTempBean?) {
        //现场记录数据——宫格数据 key-value显示

        with(holder.itemView.findViewById<TextView>(R.id.template_name)){
            text = item?.templateName ?:"模板名称"
        }

        with(holder.itemView.findViewById<RecyclerView>(R.id.table_record_rv)){
            layoutManager = GridLayoutManager(context,4)
            adapter = TableChildAdapter(context, TableChildAdapter.RECORD_TYPE_GRID,
                item?.recordData?: mutableListOf()
            )
        }

        //采样数据的具体值
        with(holder.itemView.findViewById<RecyclerView>(R.id.table_sample_value)){
            layoutManager = LinearLayoutManager(context,
                LinearLayoutManager.VERTICAL,false)
            adapter = TableChildAdapter(context,
                TableChildAdapter.SAMPLE_TYPE_LIN,
                item?.sampleData?: mutableListOf()
            )
        }
    }

    override fun onCreateViewHolder(
        context: Context,
        parent: ViewGroup,
        viewType: Int
    ): QuickViewHolder {
       return QuickViewHolder(LayoutInflater.from(context).inflate(R.layout.templater_table_fragment_table_rv,parent,false))
    }
}

数据实现

 val temList = mutableListOf<TableTempBean>()
        val tableBean = TableTempBean()
        //标题
        tableBean.templateName = "模板名称"
        tableBean.recordData = mutableListOf("电话","15787","名字","刘")
        tableBean.sampleData = mutableListOf(mutableListOf("地点","编号","时间","温度","感官","状况"),
            mutableListOf("厦门","01","21:00","温度1","感官1","状况1"),
            mutableListOf("泉州","02","20:00","温度2","感官2","状况2"),
            mutableListOf("福州","03","18:00","温度3","感官3","状况3"),
            mutableListOf("龙岩","04","15:00","温度4","感官4","状况4"),
            mutableListOf("漳州","05","12:00","温度5","感官5","状况5")
        )
        temList.add(tableBean)

        with(binding.templeRv){
            val temAdapter = TableParentAdapter()
            layoutManager = LinearLayoutManager(context,LinearLayoutManager.VERTICAL,false)
            adapter = temAdapter
              temAdapter.submitList(temList)

        }

页面布局

主页面(templater_table_fragment.xml)

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout 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=“match_parent”>

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/temple_rv"
    android:layout_width="match_parent"
    android:layout_height="500dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
     />

</androidx.constraintlayout.widget.ConstraintLayout>

嵌套页面(templater_table_fragment_table_rv.xml)

两个recyclerView控件直接实现会导致滑动出错,将所有数据加载出来,使用ScrollView实现垂直方向的滑动。(要是有其他更好地实现方式实现可以告诉我就更好啦)

<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/template_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="@dimen/dp_10"
        android:text="模板名称"
        android:textSize="@dimen/dp_20"
        android:textStyle="bold" />
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/table_record_rv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toBottomOf="@id/template_name" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/table_sample_value"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toBottomOf="@id/table_record_rv"

                />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

嵌套子页面布局(template_table_record_recycler_view.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/table_record_key"
        android:layout_width="@dimen/table_text_width"
        android:layout_height="@dimen/table_text_height"
        android:gravity="center"
        android:background="@drawable/table_adapter_bg"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textSize="@dimen/table_text_size"
        android:text="企业代表或代理人签章" />
    <app.environmental.testing.ui.adapter.SyncHorizontalScrollRecyclerView
        android:id="@+id/table_sample_key_rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/table_record_key"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

关键代码实现:Android 使用三个Reyclerview 实现表格布局,支持横向纵向滑动

相关文章:

  • 音频转文本:如何识别音频成文字
  • Maven超级详细安装部署
  • 第十四届蓝桥杯大赛软件赛国赛Python大学B组题解
  • 在 Q3D 中提取汇流条电感
  • Google Chrome下载受限制的解决方案【方法指南】
  • 【原创】vue-element-admin-plus完成确认密码功能,并实时获取Form中表单字段中的值
  • openlayers入门01 -- 环境配置和初始化地图
  • 今日行情明日机会——20250410
  • OceanBase单机版保姆级安装
  • MPP 架构解析:原理、核心优势与对比指南
  • JQuery初步学习
  • 多点:分布式升级助力新零售转型,成本节省超80% | OceanBase 案例
  • Vue 3 中 ref 与 reactive 的对比
  • window实现多jdk共存、便捷切换
  • AWS云安全实践:基于CISA关键措施的检测与实施指南
  • Spring Boot 线程池配置详解
  • Docker 介绍 · 安装详细教程
  • pycharm中安装Charm-Crypto
  • PostgreSQL-常用命令
  • 未来杭州:科技与茶香交织的生态诗篇