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 实现表格布局,支持横向纵向滑动