|
@@ -1,279 +0,0 @@
|
|
|
-package xn.xxp.widget
|
|
|
-
|
|
|
-import android.view.ViewGroup
|
|
|
-import androidx.recyclerview.widget.RecyclerView
|
|
|
-
|
|
|
-/**
|
|
|
- * info
|
|
|
- *
|
|
|
- * @author ReiChin_
|
|
|
- */
|
|
|
-class LooperLayoutManager(
|
|
|
- @RecyclerView.Orientation
|
|
|
- private val orientation: Int = RecyclerView.VERTICAL
|
|
|
-) : RecyclerView.LayoutManager() {
|
|
|
-
|
|
|
- companion object {
|
|
|
- const val TAG = "LooperLayoutManager"
|
|
|
- }
|
|
|
-
|
|
|
- override fun generateDefaultLayoutParams() = RecyclerView.LayoutParams(
|
|
|
- ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
|
- ViewGroup.LayoutParams.WRAP_CONTENT
|
|
|
- )
|
|
|
-
|
|
|
- override fun isAutoMeasureEnabled() = true
|
|
|
-
|
|
|
- override fun canScrollHorizontally() = orientation == RecyclerView.HORIZONTAL
|
|
|
-
|
|
|
- override fun canScrollVertically() = orientation == RecyclerView.VERTICAL
|
|
|
-
|
|
|
- override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
|
|
|
- if (itemCount <= 0) {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- // preLayout主要支持动画,直接跳过
|
|
|
- if (state.isPreLayout) {
|
|
|
- return
|
|
|
- }
|
|
|
- // 将视图分离放入scrap缓存中,以准备重新对view进行排版
|
|
|
- detachAndScrapAttachedViews(recycler)
|
|
|
- var autalLength = 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的宽高进行布局
|
|
|
- autalLength += if (orientation == RecyclerView.VERTICAL) {
|
|
|
- layoutDecorated(itemView, 0, autalLength, width, autalLength + height)
|
|
|
- height
|
|
|
- } else {
|
|
|
- layoutDecorated(itemView, autalLength, 0, autalLength + width, height)
|
|
|
- width
|
|
|
- }
|
|
|
-
|
|
|
- //如果当前布局过的itemView的宽度或高度总和大于RecyclerView的宽(水平)或高(垂直),则不再进行布局
|
|
|
- if (orientation == RecyclerView.VERTICAL) {
|
|
|
- if (autalLength > getHeight()) {
|
|
|
- break
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (autalLength > getWidth()) {
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun scrollHorizontallyBy(
|
|
|
- dx: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ): Int {
|
|
|
- if (orientation == RecyclerView.VERTICAL) {
|
|
|
- return 0
|
|
|
- }
|
|
|
- //1.左右滑动的时候,填充子view
|
|
|
- val travl = fillHorizontal(dx, recycler, state)
|
|
|
- if (travl == 0) {
|
|
|
- return 0
|
|
|
- }
|
|
|
-
|
|
|
- //2.滚动
|
|
|
- offsetChildrenHorizontal(travl * -1)
|
|
|
-
|
|
|
- //3.回收已经离开界面的
|
|
|
- recyclerHorizontalHideView(dx, recycler, state)
|
|
|
- return travl
|
|
|
- }
|
|
|
-
|
|
|
- override fun scrollVerticallyBy(
|
|
|
- dy: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ): Int {
|
|
|
- if (orientation == RecyclerView.HORIZONTAL) {
|
|
|
- return 0
|
|
|
- }
|
|
|
-
|
|
|
- //1.上下滑动的时候,填充子view
|
|
|
- val travl = fillVertical(dy, recycler, state)
|
|
|
- if (travl == 0) {
|
|
|
- return 0
|
|
|
- }
|
|
|
-
|
|
|
- //2.滚动
|
|
|
- offsetChildrenVertical(travl * -1)
|
|
|
-
|
|
|
- //3.回收已经离开界面的
|
|
|
- recyclerVerticalHideView(dy, recycler, state)
|
|
|
- return travl
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 左右滑动的时候,填充
|
|
|
- */
|
|
|
- private fun fillHorizontal(
|
|
|
- dx: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ): Int {
|
|
|
- if (dx > 0) {
|
|
|
- //标注1.向左滚动
|
|
|
- val lastView = getChildAt(childCount - 1) ?: return 0
|
|
|
- val lastPos = getPosition(lastView)
|
|
|
- //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
|
|
|
- if (lastView.right < width) {
|
|
|
- //标注3.判断可见的最后一个itemView的索引,
|
|
|
- // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
|
|
|
- val scrap = if (lastPos == itemCount - 1) {
|
|
|
- recycler.getViewForPosition(0)
|
|
|
- } else {
|
|
|
- recycler.getViewForPosition(lastPos + 1)
|
|
|
- }
|
|
|
- //标注4.将新的itemViewadd进来并对其测量和布局
|
|
|
- addView(scrap)
|
|
|
- measureChildWithMargins(scrap, 0, 0)
|
|
|
- val width = getDecoratedMeasuredWidth(scrap)
|
|
|
- val height = getDecoratedMeasuredHeight(scrap)
|
|
|
- val insetsWidth = width - scrap.measuredWidth
|
|
|
- layoutDecorated(
|
|
|
- scrap, lastView.right + insetsWidth, 0,
|
|
|
- lastView.right + width + insetsWidth, height
|
|
|
- )
|
|
|
- return dx
|
|
|
- }
|
|
|
- } else {
|
|
|
- //向右滚动
|
|
|
- val firstView = getChildAt(0) ?: return 0
|
|
|
- val firstPos = getPosition(firstView)
|
|
|
- if (firstView.left >= 0) {
|
|
|
- val scrap = if (firstPos == 0) {
|
|
|
- recycler.getViewForPosition(itemCount - 1)
|
|
|
- } else {
|
|
|
- recycler.getViewForPosition(firstPos - 1)
|
|
|
- }
|
|
|
- addView(scrap, 0)
|
|
|
- measureChildWithMargins(scrap, 0, 0)
|
|
|
- val width = getDecoratedMeasuredWidth(scrap)
|
|
|
- val height = getDecoratedMeasuredHeight(scrap)
|
|
|
- layoutDecorated(
|
|
|
- scrap, firstView.left - width, 0,
|
|
|
- firstView.left, height
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- return dx
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 上下滑动的时候,填充
|
|
|
- */
|
|
|
- private fun fillVertical(
|
|
|
- dy: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ): Int {
|
|
|
- if (dy > 0) {
|
|
|
- //标注1.向上滚动
|
|
|
- val lastView = getChildAt(childCount - 1) ?: return 0
|
|
|
- val lastPos = getPosition(lastView)
|
|
|
- //标注2.可见的最后一个itemView完全滑进来了,需要补充新的
|
|
|
- if (lastView.bottom < height) {
|
|
|
- //标注3.判断可见的最后一个itemView的索引,
|
|
|
- // 如果是最后一个,则将下一个itemView设置为第一个,否则设置为当前索引的下一个
|
|
|
- val scrap = if (lastPos == itemCount - 1) {
|
|
|
- recycler.getViewForPosition(0)
|
|
|
- } else {
|
|
|
- recycler.getViewForPosition(lastPos + 1)
|
|
|
- }
|
|
|
- //标注4.将新的itemViewadd进来并对其测量和布局
|
|
|
- addView(scrap)
|
|
|
- measureChildWithMargins(scrap, 0, 0)
|
|
|
- val width = getDecoratedMeasuredWidth(scrap)
|
|
|
- val height = getDecoratedMeasuredHeight(scrap)
|
|
|
- val insetsHeight = height - scrap.measuredHeight
|
|
|
- layoutDecorated(
|
|
|
- scrap, 0, lastView.bottom + insetsHeight,
|
|
|
- width, lastView.bottom + height + insetsHeight
|
|
|
- )
|
|
|
- return dy
|
|
|
- }
|
|
|
- } else {
|
|
|
- //向下滚动
|
|
|
- val firstView = getChildAt(0) ?: return 0
|
|
|
- val firstPos = getPosition(firstView)
|
|
|
- if (firstView.top >= 0) {
|
|
|
- val scrap = if (firstPos == 0) {
|
|
|
- recycler.getViewForPosition(itemCount - 1)
|
|
|
- } else {
|
|
|
- recycler.getViewForPosition(firstPos - 1)
|
|
|
- }
|
|
|
- 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 dy
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 回收界面不可见的view
|
|
|
- */
|
|
|
- private fun recyclerHorizontalHideView(
|
|
|
- dx: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ) {
|
|
|
- for (i in 0 until childCount) {
|
|
|
- val view = getChildAt(i) ?: continue
|
|
|
- if (dx > 0) {
|
|
|
- //向左滚动,移除一个左边不在内容里的view
|
|
|
- if (view.right < 0) {
|
|
|
- removeAndRecycleView(view, recycler)
|
|
|
- }
|
|
|
- } else {
|
|
|
- //向右滚动,移除一个右边不在内容里的view
|
|
|
- if (view.left > width) {
|
|
|
- removeAndRecycleView(view, recycler)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 回收界面不可见的view
|
|
|
- */
|
|
|
- private fun recyclerVerticalHideView(
|
|
|
- dy: Int,
|
|
|
- recycler: RecyclerView.Recycler,
|
|
|
- state: RecyclerView.State
|
|
|
- ) {
|
|
|
- for (i in 0 until childCount) {
|
|
|
- val view = getChildAt(i) ?: continue
|
|
|
- if (dy > 0) {
|
|
|
- //向上滚动,移除一个上边不在内容里的view
|
|
|
- if (view.bottom < 0) {
|
|
|
- removeAndRecycleView(view, recycler)
|
|
|
- }
|
|
|
- } else {
|
|
|
- //向下滚动,移除一个下边不在内容里的view
|
|
|
- if (view.top > height) {
|
|
|
- removeAndRecycleView(view, recycler)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-}
|