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
/**
-
自动播放的工具
*/
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 = 0constructor(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 = 0override 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
}