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  烟花易冷心易碎  阅读(4)  评论(0编辑  收藏  举报