recycleview 滑动辅助

import android.content.Context
import android.graphics.Rect
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.delay
import java.lang.ref.SoftReference
import kotlin.math.abs
import kotlin.math.min
import kotlin.math.sqrt

/**

  • https://www.jianshu.com/p/9ec5098655b7

  • 自动播放的工具
    */
    class AutoPlayTool {
    val tag = "AutoPlayTool"

    //辅助接口
    public interface ISelectItemListener {
    /**
    * 参数1与参数2相等则是同一个,无需处理
    * 参数1与参数2不相等,应该停止掉参数2,播放参数1
    */
    fun onItem(new: Int, old: Int)
    }

    private var softSelectItemListener: SoftReference? = null
    private var visiblePercent = 1

    /**
    *热点区域,最接近这个位置的则锚定
    */
    var anchorPoint = 0

    constructor(visiblePercent: Int = 1) {
    this.visiblePercent = visiblePercent
    }

    /**

    • 当滑动停止的时候,开始视频播放
    • @param recyclerView
    • @return
      */
      @Deprecated("")
      fun onActiveWhenNoScrolling(recyclerView: RecyclerView, y: Int): Int {
      val index = calculateIndex(recyclerView, y)
      smoothScrollToPosition(calculateIndex(recyclerView, y))
      return index
      }

    /**

    • 获取可见百分比
    • @param v
    • @return
      */
      private fun getVisiblePercent(v: View): Int {
      val r = Rect()
      val visible = v.getLocalVisibleRect(r)
      if (visible && v.measuredHeight > 0) {
      val percent = 100 * r.height() / v.measuredHeight
      return percent
      }
      return -1
      }

    private fun getVisible(v: View, value: Int): Boolean {
    val r = Rect()
    val visible = v.getLocalVisibleRect(r)
    if (visible && v.visibility == View.VISIBLE) {
    return if (getVisiblePercent(v) >= value) {
    true
    } else {
    false
    }
    }
    return false
    }

    var ss: RecyclerView.OnScrollListener = object : RecyclerView.OnScrollListener() {
    var maxY: Int = 0
    var talY: Int = 0

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
    super.onScrollStateChanged(recyclerView, newState)
    if (newState == RecyclerView.SCROLL_STATE_IDLE) {
    recyclerView.removeOnScrollListener(this)
    smoothScrollToPosition(calculateIndex(recyclerView, talY))
    // onActiveWhenNoScrolling(recyclerView, talY)
    maxY = 0
    talY = 0
    }
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
    super.onScrolled(recyclerView, dx, dy)
    Log.d("定位", "onScrolled Y=\(dy") if (abs(dy.toDouble()) > abs(maxY.toDouble())) { maxY = dy } talY += dy } } var lastPlay = -1 fun smoothScrollToPosition(position: Int, finish: (() -> Unit)? = null) { val viewLocation = IntArray(2) withRecyclerView.getLocationOnScreen(viewLocation) Log.d(tag, "锚定位置\){anchorPoint} 当前RecycleView Y=" + viewLocation[1])
    withRecyclerView.smoothScrollToPositionWithOffset(position, anchorPoint - viewLocation[1]) {
    softSelectItemListener?.get()?.onItem(position, lastPlay)
    Log.d(tag, "新位置\(position 旧位置\)lastPlay")
    lastPlay = position
    finish?.invoke()
    }
    }

    fun smoothScrollToPosition1(position: Int, finish: (() -> Unit)? = null) {
    val viewLocation = IntArray(2)
    withRecyclerView.getLocationOnScreen(viewLocation)
    Log.d(tag, "锚定位置\({anchorPoint} 当前RecycleView Y=" + viewLocation[1]) withRecyclerView.smoothScrollToPositionWithOffset1( position, anchorPoint - viewLocation[1] ) { softSelectItemListener?.get()?.onItem(position, lastPlay) Log.d(tag, "新位置\)position 旧位置$lastPlay")
    lastPlay = position
    finish?.invoke()
    }
    }

    private lateinit var withRecyclerView: RecyclerView
    fun attachToRecyclerView(recyclerView: RecyclerView) {
    withRecyclerView = recyclerView
    //增加一层代理,防止移动过程中造成的自动触发跳屏
    recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
    super.onScrollStateChanged(recyclerView, newState)
    if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
    recyclerView.removeOnScrollListener(ss)
    recyclerView.addOnScrollListener(ss)
    }
    }
    })
    }

    fun withToRecyclerView(recyclerView: RecyclerView) {
    withRecyclerView = recyclerView
    }

    /**

    • 计算位置,滑动方向影响计算方式
    • @param recyclerView
    • @param direction 0表示未滑动,1表示向上滑(内容底部到顶部),-1表示下拉(内容从顶部到底部)
    • @return
      */
      fun calculateIndex(recyclerView: RecyclerView, direction: Int = 0): Int {
      var layoutManager: LinearLayoutManager? = null
      var position = -1
      if (recyclerView.layoutManager is LinearLayoutManager) {
      layoutManager = recyclerView.layoutManager as LinearLayoutManager?
      }
      val viewLocation = IntArray(2)
      recyclerView.getLocationOnScreen(viewLocation)
      Log.d(tag, "RecycleView Y=" + viewLocation[1])
      if (layoutManager != null) {
      var firstItemPosition = layoutManager.findFirstVisibleItemPosition()
      val lastItemPosition = layoutManager.findLastVisibleItemPosition()
      val items: LinkedHashMap<Int, RecyclerView.ViewHolder> = LinkedHashMap()
      while (firstItemPosition <= lastItemPosition) {
      val holder = recyclerView.findViewHolderForLayoutPosition(firstItemPosition)
      val view = holder?.itemView
      if (view != null && getVisible(view, visiblePercent)) {
      items[firstItemPosition] = holder
      }
      firstItemPosition++
      }
      //最小距离
      var minDistance = Int.MAX_VALUE
      //找出距离中间最近的一个
      for ((key, value) in items) {
      val itemDistance = calculatedDistance(value.itemView, direction = direction)
      if (itemDistance < minDistance) {
      minDistance = itemDistance
      position = key
      }
      }
      }
      return position
      }

    /**

    • 计算视图距离屏幕中心的距离。

    • 该方法通过获取视图在屏幕上的位置,然后计算该位置与屏幕中心垂直距离的绝对值。

    • 这样做的目的是为了评估视图与屏幕中心的相对位置,用于某些基于位置的布局或交互逻辑。

    • @param view 要计算距离的视图对象。

    • @param point 屏幕目标绝对位置。

    • @param direction 0表示未滑动,1表示向上滑(内容底部到顶部),-1表示下拉(内容从顶部到底部)

    • @return 视图距离屏幕中心的垂直距离的绝对值。
      */
      private fun calculatedDistance(view: View, point: Int = anchorPoint, direction: Int = 0): Int {
      val viewLocation = IntArray(2)
      view.getLocationOnScreen(viewLocation)
      //检测用户意图偏移量
      val tagPoint = when (direction) {
      in 1..Int.MAX_VALUE -> { //上滑动
      point + (view.height * 0.8).toInt()
      }

      in Int.MIN_VALUE..(-1) -> {//拉下滑动
      // 处理小于等于-1的情况
      point - (view.height * 0.8).toInt()
      }

      else -> {
      point
      }
      }
      return abs((viewLocation[1] - tagPoint).toDouble()).toInt()
      }

}

private fun RecyclerView.smoothScrollToPositionWithOffset(
position: Int,
offset: Int,
finish: (() -> Unit)? = null
) {
val linearSmoothScroller = object : LinearSmoothScroller(context) {
override fun onTargetFound(
targetView: View,
state: RecyclerView.State,
action: Action
) {
super.onTargetFound(targetView, state, action)
val dx = calculateDxToMakeVisible(targetView, horizontalSnapPreference)
val dy = calculateDyToMakeVisible(targetView, SNAP_TO_START)

val distance = sqrt((dx * dx + dy * dy).toDouble()).toInt()
val time = calculateTimeForDeceleration(distance)
if (time > 0) {
action.update(-dx, -dy - offset, time, mDecelerateInterpolator)
}
}

override fun onStop() {
super.onStop()
finish?.invoke()
}
}
linearSmoothScroller.targetPosition = position
layoutManager?.startSmoothScroll(linearSmoothScroller)
}

private fun RecyclerView.smoothScrollToPositionWithOffset1(
position: Int,
offset: Int,
finish: (() -> Unit)? = null
) {
(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(position, offset)
post { finish?.invoke() }

}

/**

  • 寻找完全可见的item
  • @param last 是否是最后一个
  • @return 下标
    */
    fun RecyclerView.findCompletelyVisibleItemPosition(last: Boolean = false): Int {
    var completelyVisibleItemPosition = -1
    // 滑动结束,获取第一个完全可见的 item
    val layoutManager = layoutManager
    if (layoutManager is LinearLayoutManager) {
    // 对于 LinearLayoutManager
    completelyVisibleItemPosition =
    if (last) layoutManager.findLastCompletelyVisibleItemPosition() else layoutManager.findFirstCompletelyVisibleItemPosition()
    } else if (layoutManager is GridLayoutManager) {
    completelyVisibleItemPosition =
    if (last) layoutManager.findLastCompletelyVisibleItemPosition() else layoutManager.findFirstCompletelyVisibleItemPosition()
    }
    return completelyVisibleItemPosition
    }

/**

  • 获取可见百分比
  • @param v
  • @return
    */
    fun View.getVisiblePercent(): Int {
    val r = Rect()
    val visible = getLocalVisibleRect(r)
    if (visible && measuredHeight > 0) {
    val percent = 100 * r.height() / measuredHeight
    return percent
    }
    return 0
    }
posted @ 2024-11-01 10:36  烟花易冷心易碎  阅读(49)  评论(0)    收藏  举报