【Android笔记】RecycleView

532 阅读7分钟

一、优秀的ListAdapter

1、拒绝手动Notifydatasetchanged(),使用ListAdapter高效完成RecyclerView刷新 - 掘金 (juejin.cn)

  1. DiffUtil非常牛逼,里面可以对比View,若数据源所对比的东西不同,则会重新绑定当前View的数据(刷新),所以即使不处理条目复用,复用时也会自动刷新。
  2. 设计到列表的更新需使用新列表,文章里的**toMutableList()**介绍:Returns a new MutableList filled with all elements of this collection. 看了下对象名,确实是新的,这个贼好用。

2、submitList不生效解决方案:重写方法

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,容器不刷新

三、生命周期

1、 如何在Adapter中优雅的使用Context

2、 onViewAttachedToWindow执行时机在onCreateViewHolder后

3、 关于RecyclerView你知道的不知道的都在这了

  1. adapter 可实现的父类方法中 关于回收
  • onBindViewHolderonViewRecycled对应,一创,一收;
  • onViewAttachedToWindowonViewDetachedFromWindow:只有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 等分间距

  1. 适用于规则item个人感觉和瀑布流的主要区别就是这个;
  2. 其他Layout的方法类似: RecyclerView禁止滑动

3、NestedScrollView+RecyclerView优雅的解决滑动冲突问题

4、RecyclerView加载动画设置

五、辅助类

一、间距

1、Decoration控制条目:Recycleview自主选择两边和上下左右的边距

2、控制item与adapter间距控制:

3、 clipChildren

  1. 如果RecycleView设置了padding,滑动时那块位置是无法占用的,白边,这个时候给其父布局也增加这个就好了;
  android:clipChildren="false"
  android:clipToPadding="false"

二、曝光与定位

1、判断可见性方法

  1. findFirstCompletelyVisibleItemPosition 找不到会返回-1,所以在大个item间,第1个和第2个各露出一半的时候就会返回-1;
  2. getGlobalVisibleRect() 与 getLocalVisibleRect()
  • getGlobalVisibleRect() 是view可见区域相对于屏幕来说的坐标位置.
  • getLocalVisibleRect()是view可见区域相对于自己坐标的位置.

2、定位

####################################################################

三、其他问题

1、Android RecyclerView隐藏item(多布局)的所在区域显示空白

1、 四种定位滚动与居中问题

  1. Smooth居中,复写CenterLayoutManage

  2. 两种居中解释

  3. 滚动与刷新是两个概念,详解

  4. android recycleview跳转到指定位置scrollToPosition与scrollToPositionWithOffset的区别

  5. CliptoPadding对其的影响图解

  • 当设置为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间距]

  1. 获取RecyclerView当前屏幕显示的第一个条目的position位置

  2. 局部与全局刷新,防止闪动

  3. RecyclerView瀑布流的那些坑
    4.1 左右高度不一致时使用params.spanIndex取代position进行左右判断。 4.2 动画效果处理

  4. 两个RecycleView联动思路,一般是使用ViewPager PS:viewpage

  5. RecycleView 通过smooth移动到position,使用layoutManger可以FindViewByPosition

  6. RecyclerView滑动监听,判断是否滑动到了最后一个item

6.1 判断RecyclerView滑动到底部和顶部

  1. addItemDecoration

7.1 RecyclerView ItemDecoration 完全解析

7.2 val position = parent.getChildAdapterPosition(view)挺好用的 StaggeredGridLayoutManager自带getSpanIndex() 1)Android瀑布流item间隔问题_tobevan的博客-程序员秘密_android 瀑布流间距

  1. 居中辅助运算,核心代码

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禁止滑动

  1. 指定layoutManager
  2. 拦截点击事件
recyclerView.setOnTouchListener { _, _ ->
    true // 返回 true 表示消费了这个事件,不传递给下层 View 处理
}

4、 自定义LayoutManager 实现弧形以及滑动放大效果RecyclerView

四、实践情况

1、 相邻View有UI连接的,有item从后往前判断是或许会简单的多;

2、多布局封装的adapter【BaseQuickAdapter】

3、刁钻的埋点与可见View

1、曝光埋点方案:recyclerView中的item曝光逻辑实现_recycleview点击item高亮显示

2、Android 曝光采集(商品view曝光量的统计)

3、常见的xml配置

<androidx.recyclerview.widget.RecyclerView
    xxxxxxx
    android:orientation="horizontal"
    tools:listitem="@layout/item_sugar" />

五、Crash

1、 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打造自动循环效果