一、优秀的ListAdapter
1、拒绝手动Notifydatasetchanged(),使用ListAdapter高效完成RecyclerView刷新 - 掘金 (juejin.cn)
- DiffUtil非常牛逼,里面可以对比View,若数据源所对比的东西不同,则会重新绑定当前View的数据(刷新),所以即使不处理条目复用,复用时也会自动刷新。
- 设计到列表的更新需使用新列表,文章里的**toMutableList()**介绍:Returns a new MutableList filled with all elements of this collection. 看了下对象名,确实是新的,这个贼好用。
override fun submitList(list: MutableList<bean>?) {
super.submitList(
if (list != null) {
ArrayList(list)
} else {
null
}
)
}
二、实用方法
1、位置相关
1、 已知holder获取位置 holder.adapterPosition(ps:初始下标都是偏移量0),被弃用了;
- getBindindAdapterPosition:获取当前adapter中item位置;
- getAbsoluteAdapterPosition:多个adapter中获取绝对位置;
2、 已知bean/item获取位置 getItemPosition,onbinderViewHolder中可以通过getItem(position)获取bean。
3、getCurrentList():获取列表,像是listAdapter的能力;
4、setHasFixedSize:好东西,优化项,如果recycleview的宽高不会变就把这个设置为true,容器不刷新
三、生命周期
2、 onViewAttachedToWindow执行时机在onCreateViewHolder后
- adapter 可实现的父类方法中 关于回收
onBindViewHolder与onViewRecycled对应,一创,一收;onViewAttachedToWindow与onViewDetachedFromWindow:只有view漏一点点就会Attached,全没了才会Detached;- Holder:VideoViewHolder{bc17f90 position=0 id=-1, oldPos=-1, pLpos:-1 no parent}
- 实践发现 Viewpager[预加载默认1] 配合adapter,6条item,依次上下滑动,第三条可能不会被回收onViewRecycled,则不管怎么滑动时,下次也不会onBindViewHolder;
四、 View组合
1、 ScrollView嵌套RecycleView setFocusableInTouchMode:RecyclerView常见问题解决方案,RecyclerView嵌套自动滚动,RecyclerView 高度设置wrap_content 无作用等问题
2、 网格布局 GridLayoutManager:RecyclerView GridLayoutManager 等分间距
- 适用于规则item个人感觉和瀑布流的主要区别就是这个;
- 其他Layout的方法类似: RecyclerView禁止滑动
3、NestedScrollView+RecyclerView优雅的解决滑动冲突问题
五、辅助类
一、间距
1、Decoration控制条目:Recycleview自主选择两边和上下左右的边距
2、控制item与adapter间距控制:
- 样例:Android Recyclerview设置Item之间的间距
- margin和padding可以设置负数:android:layout_marginHorizontal="-8dp"
- 尽量用padding;
3、 clipChildren
- 如果RecycleView设置了padding,滑动时那块位置是无法占用的,白边,这个时候给其父布局也增加这个就好了;
android:clipChildren="false"
android:clipToPadding="false"
二、曝光与定位
1、判断可见性方法
- findFirstCompletelyVisibleItemPosition 找不到会返回-1,所以在大个item间,第1个和第2个各露出一半的时候就会返回-1;
- getGlobalVisibleRect() 与 getLocalVisibleRect()
- getGlobalVisibleRect() 是view可见区域相对于屏幕来说的坐标位置.
- getLocalVisibleRect()是view可见区域相对于自己坐标的位置.
2、定位
####################################################################
三、其他问题
1、Android RecyclerView隐藏item(多布局)的所在区域显示空白
1、 四种定位滚动与居中问题
- 当设置为false时,当RecyclerView滑动时,会使得RV的绘制区域忽略设置的paddingTop值;true的话则顶部会有paddingtop(如果写了padding=“xxx”的话),所以false时,仅仅影响padding top与bottom,对left,right无影响。
- 当前设置了false时,而且overScrollMode的值不是never时,RV的边界阻尼阴影会忽略所有的padding效果(即:针对l,t,r,b四边的padding都有效)
2、 [RecycleView间距]
-
RecyclerView瀑布流的那些坑
4.1 左右高度不一致时使用params.spanIndex取代position进行左右判断。 4.2 动画效果处理 -
RecycleView 通过smooth移动到position,使用layoutManger可以FindViewByPosition
- addItemDecoration
7.1 RecyclerView ItemDecoration 完全解析
7.2 val position = parent.getChildAdapterPosition(view)挺好用的 StaggeredGridLayoutManager自带getSpanIndex() 1)Android瀑布流item间隔问题_tobevan的博客-程序员秘密_android 瀑布流间距
- 居中辅助运算,核心代码
mLayoutManager?.scrollToPositionWithOffset(pos,offset)
public void smoothScrollToCenter(final RecyclerView recyclerView, final int position) {
recyclerView.post(new Runnable() {
@Override
public void run() {
// 获取 RecyclerView 的 LayoutManager
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager != null) {
// 获取目标项的视图
View view = layoutManager.findViewByPosition(position);
if (view != null) {
// 获取目标项的高度
int itemHeight = view.getHeight();
// 计算目标项的顶部位置
int top = view.getTop();
// 获取 RecyclerView 的高度
int recyclerHeight = recyclerView.getHeight();
// 计算滚动的偏移
int offset = (recyclerHeight / 2) - (itemHeight / 2) - top;
// 使用 smoothScrollBy 方法滚动
recyclerView.smoothScrollBy(0, offset);
}
}
}
});
}
使用示例:
smoothScrollToCenter(recyclerView, pos);
- 水平方向居中
public void smoothScrollToCenterHorizontal(final RecyclerView recyclerView, final int position) {
recyclerView.post(new Runnable() {
@Override
public void run() {
// 获取 RecyclerView 的 LayoutManager
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager != null) {
// 获取目标项的视图
View view = layoutManager.findViewByPosition(position);
if (view != null) {
// 获取目标项的宽度
int itemWidth = view.getWidth();
// 计算目标项的左侧位置
int left = view.getLeft();
// 获取 RecyclerView 的宽度
int recyclerWidth = recyclerView.getWidth();
// 计算滚动的偏移
int offset = (recyclerWidth / 2) - (itemWidth / 2) - left;
// 使用 smoothScrollBy 方法滚动
recyclerView.smoothScrollBy(offset, 0);
}
}
}
});
}
3、 RecycleView禁止滑动
- 指定layoutManager
- 拦截点击事件
recyclerView.setOnTouchListener { _, _ ->
true // 返回 true 表示消费了这个事件,不传递给下层 View 处理
}
4、 自定义LayoutManager 实现弧形以及滑动放大效果RecyclerView
- RecyclerView 的三种LayoutManager
- 一些神奇的功能也能用LayoutManager实现,例如独特的居中的文字样式
四、实践情况
1、 相邻View有UI连接的,有item从后往前判断是或许会简单的多;
2、多布局封装的adapter【BaseQuickAdapter】
3、刁钻的埋点与可见View
1、曝光埋点方案:recyclerView中的item曝光逻辑实现_recycleview点击item高亮显示
3、常见的xml配置
<androidx.recyclerview.widget.RecyclerView
xxxxxxx
android:orientation="horizontal"
tools:listitem="@layout/item_sugar" />
五、Crash
1、 Invalid view holder adapter:往往是更新不及时导致的;
- 及时刷新:当RecyclerView遇到Inconsistency detected崩溃时
- Try-Catch方案:RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解决方案
六、ViewPager
1、ViewPager 超详解:玩出十八般花样,ViewPager其实第一个子View是RecycleView。
七、样例
//RecycleView素质三连
class SugarAdapter : ListAdapter<String, SugarAdapter.CustomViewHolder>(object : DiffUtil.ItemCallback<String>() {
override fun areItemsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
@SuppressLint("DiffUtilEquals")
override fun areContentsTheSame(oldItem: String, newItem: String): Boolean {
return oldItem == newItem
}
}) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ItemSugarBinding = DataBindingUtil.inflate(inflater, R.layout.item_sugar, parent, false)
return CustomViewHolder(binding)
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
val item = getItem(position)
holder.bind(item, position)
}
inner class CustomViewHolder(private val binding: ItemSugarBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: String, position: Int) {
binding.tvValue.text = item
binding.executePendingBindings()
}
}
}
val adapter1 = SugarAdapter()
mBindView.rvSystolic.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
this.adapter = adapter1
setOnTouchListener { _, _ ->
true // 返回 true 表示消费了这个事件,不传递给下层 View 处理
}
}
dataList = (20..300).map { it.toString() } as ArrayList<String>
adapter1.submitList(dataList)
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_systolic"
android:layout_width="283dp"
android:layout_height="67dp"
android:layout_marginTop="11dp"
android:background="@drawable/bg_rv_sugar"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title_systolic_sub"
tools:listitem="@layout/item_sugar" />
item_sugar
<?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="140dp"
>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_tools_1"
android:drawablePadding="4dp"
android:gravity="center"
android:textColor="@color/white"
android:textSize="0.01sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="测试" />
</androidx.constraintlayout.widget.ConstraintLayout>
八、其他
1、Android RecyclerView打造自动循环效果
- layoutManagr设置横竖向,可以自动滚动,也可以手动控制为水平滑动抽奖
- 无限循环RecyclerView的完美解决方案