1、IM类的会话列表类似微信,底部输入框,顶部标题等,中间这一块都是rv列表展示。
2、中间的rv是必须定位为wrap_content,无法设置match_parent。
3、数据必须是倒序摆放,下拉刷新时候加载更多。
我们知道类似布局管理器:
//第三个参数 true 数据跟布局倒序,从底部开始 你的position==0 是在最低部,跟平時相反
var linearLayoutManager = LinearLayoutManager(this,RecyclerView.VERTICAL, false)
我写了个简单demo测试了一下,发现下拉刷新去加载更多数据的时候总是会自动往下展示一点点。
设置了之后如果是下拉刷新去加载更多数据,就像上面的gif中所显示一样。下面还有测试数据0项可以滑动,最顶部还有测试数据19项可以滑动,这不坑爹吗?居然显示在中间。
换另外一种方式来实现:linearLayoutManager.stackFromEnd = true
测试结果还是不错的,符合预期。
目前我的IM类项目就是使用这种去实现的,但是出现了一个bug,类似上面GIF图片一样,加载更多之后不是停留在之前的位置,总是自动往上滑动了几项。
布局从下往上开始绘制,数据跟平时展示还是一样,最终数据集里面最后一个数据还是显示在最底部,当超过一屏的时候,默认一进来类似默认滑动到最底部的效果类似。
应用场景:IM聊天会话界面
当布局文件非常复杂的时候,下拉刷新去加载更多历史数据的时候,总是会不自觉往下滑动少许,也就是网上说的刷新rv时候自动滑动的情况。
网上提出一些解决方法,把rv的高度自适应改为match_parent,但是在当前IM项目的应用场景里面,rv的高度必须是wrap_content,这就无解了。
屏蔽linearLayoutManager.stackFromEnd = true,刷新之后自动滑动的bug就没了,但是当开始进来默认数据超过一屏的时候,必须默认滑动到底部,从底部开始展示数据
使用scrollToPosition 会有一个滑动的闪烁更新过程,所以需要改成无感知滑动到底部:scrollToPositionWithOffset
// linearLayoutManager.stackFromEnd = true
一进来默认已经滑动到底部,那么就跟从底部开始绘制一个道理。刷新之后不会自动滑动了一段距离。
linearLayoutManager.stackFromEnd = true ,使用了这个属性之后滑动了一段距离,估计是因为我的布局太复杂导致高度计算出现问题所导致,xml代码就有五百行,实在没办法,只能把它关了。
demo代码:
class TestAdapter(context: Context, data: ArrayList<String>): RecyclerView.Adapter<TestAdapter.TestHolder>() {
private val mDiffCallback = TestDiffCallBack()
var oldData: ArrayList<String> = data
set(value) {
mDiffCallback.newData = value
val diffResult = DiffUtil.calculateDiff(mDiffCallback, false)
field.clear()
field.addAll(value)
diffResult.dispatchUpdatesTo(object : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
notifyItemRangeInserted(position, count)
}
override fun onRemoved(position: Int, count: Int) {
notifyItemRangeRemoved(position, count)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
notifyItemMoved(fromPosition, toPosition)
}
override fun onChanged(position: Int, count: Int, payload: Any?) {
notifyItemRangeChanged(position, count, null)
}
})
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestHolder
= TestHolder(LayoutInflater.from(parent.context).inflate(R.layout.test_activity_rv_item, parent,false))
override fun getItemCount(): Int = oldData.size
override fun onBindViewHolder(holder: TestHolder, position: Int) {
holder.textView.text = oldData[position]
}
inner class TestHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textView: TextView = itemView.findViewById<TextView>(R.id.txt)
}
inner class TestDiffCallBack() : DiffUtil.Callback() {
var newData: ArrayList<String> = arrayListOf()
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldData[oldItemPosition] == newData[newItemPosition]
}
override fun getOldListSize(): Int {
return oldData.size
}
override fun getNewListSize(): Int {
return newData.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return true
}
}
}
class TestActivity : AppCompatActivity(), OnRefreshListener, OnLoadMoreListener {
lateinit var refre: SmartRefreshLayout
lateinit var rv: RecyclerView
private var info: ArrayList<String> = arrayListOf()
private var adapter : TestAdapter ? = null
private var fresh: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
refre = findViewById(R.id.rc_refresh)
refre.isNestedScrollingEnabled = false
refre.setRefreshHeader(RongRefreshHeader(this))
// refre.setRefreshFooter(RongRefreshHeader(this))
refre.setEnableRefresh(true)
refre.setOnRefreshListener(this)
// refre.setOnLoadMoreListener(this)
rv = findViewById(R.id.rv)
//第三个参数 true 数据跟布局倒序,从底部开始 你的position==0 是在最低部,跟平時相反
var linearLayoutManager = LinearLayoutManager(this,RecyclerView.VERTICAL, false)
/**
* 布局从下往上开始绘制,数据跟平时展示还是一样,最终数据集里面最后一个数据还是显示在最底部,当超过一屏的时候,默认一进来类似默认滑动到最底部的效果类似。
* 应用场景:IM聊天会话界面
* 当布局文件非常复杂的时候,下拉刷新去加载更多历史数据的时候,总是会不自觉往下滑动少许,也就是网上说的刷新rv时候自动滑动的情况。
* 网上提出一些解决方法,把rv的高度自适应改为match_parent,但是在当前IM项目的应用场景里面,rv的高度必须是wrap_content,这就无解了。
* 屏蔽linearLayoutManager.stackFromEnd = true,刷新之后自动滑动的bug就没了,但是当开始进来默认数据超过一屏的时候,必须默认滑动到底部,从底部开始展示数据
* 使用scrollToPosition 会有一个滑动的闪烁更新过程,所以需要改成无感知滑动到底部:scrollToPositionWithOffset
*
*/
// linearLayoutManager.stackFromEnd = true
rv.layoutManager = linearLayoutManager
info = getData()
adapter = TestAdapter(this,info)
rv.adapter = adapter
linearLayoutManager.scrollToPositionWithOffset(info.size-1, 0)
}
private fun getData() : ArrayList<String>{
var data = arrayListOf<String>()
var size = fresh * 20
for (index in size + 19 downTo size) {
data.add("测试数据$index")
}
fresh += 1
return data
}
override fun onRefresh(refreshLayout: RefreshLayout) {
var data = getData()
data.addAll(info)
adapter?.oldData = data
refre.finishRefresh()
}
override fun onLoadMore(refreshLayout: RefreshLayout) {
}
}
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activity.TestActivity">
<io.rong.imkit.widget.refresh.SmartRefreshLayout
android:id="@+id/rc_refresh"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:background="@color/color_FFC900"
android:layout_width="match_parent"
android:layout_height="wrap_content"></androidx.recyclerview.widget.RecyclerView>
</io.rong.imkit.widget.refresh.SmartRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<?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">
<TextView
android:id="@+id/txt"
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="测试数据1"
android:gravity="center"
android:textSize="18sp"
android:textColor="@color/color_E72D2D"
></TextView>
</LinearLayout>