RecyclerView实现竖向无限循环滚动的列表

通过重写RecyclerView.LayoutManager实现

直接使用该LayoutManger即可

package com.example.testrecy.test

import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Recycler

class MyLayoutManager : RecyclerView.LayoutManager() {
    private var looperEnable = true

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        return RecyclerView.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
    }

    override fun canScrollHorizontally(): Boolean {
        return false
    }

    override fun canScrollVertically(): Boolean {
        return true
    }

    override fun onLayoutChildren(
        recycler: Recycler,
        state: RecyclerView.State
    ) {
        if (itemCount <= 0) {
            return
        }
        //preLayout主要支持动画,直接跳过
        if (state.isPreLayout) {
            return
        }
        //将视图分离放入scrap缓存中,以准备重新对view进行排版
        detachAndScrapAttachedViews(recycler)
        var actualHeight = 0
        for (i in 0 until itemCount) {
            //初始化,将在屏幕内的view填充
            val itemView = recycler.getViewForPosition(i)
            addView(itemView)
            //测量itemView的宽高
            measureChildWithMargins(itemView, 0, 0)
            val width = getDecoratedMeasuredWidth(itemView)
            val height = getDecoratedMeasuredHeight(itemView)
            //根据itemView的宽高进行布局
            layoutDecorated(itemView, 0, actualHeight, width, actualHeight + height)
            actualHeight += height
            //如果当前布局过的itemView的宽度总和大于RecyclerView的宽,则不再进行布局
            if (actualHeight > getHeight()) {
                break
            }
        }
    }

    override fun scrollVerticallyBy(
        dy: Int,
        recycler: Recycler,
        state: RecyclerView.State
    ): Int {

        //1.上下滑动的时候,填充子view
        val travel = fill(dy, recycler)
        if (travel == 0) {
            return 0
        }

        //2.滚动
        offsetChildrenVertical(travel * -1)

        //3.回收已经离开界面的
        recyclerHideView(dy, recycler)
        return travel
    }

    /**
     * 左右滑动的时候,填充
     */
    private fun fill(dy: Int, recycler: Recycler): Int {
        var translateY = dy
        if (translateY > 0) {
            //标注1.向上滚动
            val lastView = getChildAt(childCount - 1) ?: return 0
            val lastPos = getPosition(lastView)
            //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
            if (lastView.bottom < height) {
                var scrap: View? = null
                //标注3.判断可见的最后一个itemView的索引,
                // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
                if (lastPos == itemCount - 1) {
                    if (looperEnable) {
                        scrap = recycler.getViewForPosition(0)
                    } else {
                        translateY = 0
                    }
                } else {
                    scrap = recycler.getViewForPosition(lastPos + 1)
                }
                if (scrap == null) {
                    return translateY
                }
                //标注4.将新的itemView add进来并对其测量和布局
                addView(scrap)
                measureChildWithMargins(scrap, 0, 0)
                val width = getDecoratedMeasuredWidth(scrap)
                val height = getDecoratedMeasuredHeight(scrap)
                layoutDecorated(
                    scrap, 0, lastView.bottom,
                    width, lastView.bottom + height
                )
                return translateY
            }
        } else {
            //向下滚动
            val firstView = getChildAt(0) ?: return 0
            val firstPos = getPosition(firstView)
            if (firstView.top >= 0) {
                var scrap: View? = null
                if (firstPos == 0) {
                    if (looperEnable) {
                        scrap = recycler.getViewForPosition(itemCount - 1)
                    } else {
                        translateY = 0
                    }
                } else {
                    scrap = recycler.getViewForPosition(firstPos - 1)
                }
                if (scrap == null) {
                    return 0
                }
                addView(scrap, 0)
                measureChildWithMargins(scrap, 0, 0)
                val width = getDecoratedMeasuredWidth(scrap)
                val height = getDecoratedMeasuredHeight(scrap)
                layoutDecorated(
                    scrap, 0, firstView.top - height,
                    width, firstView.top
                )
            }
        }
        return translateY
    }

    /**
     * 回收界面不可见的view
     */
    private fun recyclerHideView(
        dy: Int,
        recycler: Recycler
    ) {
        for (i in 0 until childCount) {
            val view = getChildAt(i) ?: continue
            if (dy > 0) {
                //向上滚动,移除一个上边不在内容里的view
                if (view.bottom < 0) {
                    removeAndRecycleView(view, recycler)
                    Log.d(
                        TAG,
                        "循环: 移除 一个view  childCount=$childCount"
                    )
                }
            } else {
                //向下滚动,移除一个下边不在内容里的view
                if (view.top > height) {
                    removeAndRecycleView(view, recycler)
                    Log.d(
                        TAG,
                        "循环: 移除 一个view  childCount=$childCount"
                    )
                }
            }
        }
    }

    fun setLooperEnable(looperEnable: Boolean) {
        this.looperEnable = looperEnable
    }

    companion object {
        private const val TAG = "MyLayoutManager"
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章