滑动冲突

根本原因:

由于Android事件分发机制,父控件会优先拦截事件,会先抢占滑动事件,使子控件无法响应导致。

RecyclerView嵌套在ScrollView中

原因:

  1. ScrollView会优先处理滑动事件,导致RecyclerView不能正常滑动。
  2. 会产生性能问题:由于ScrollView会一次性测量所有的子View,而RecyclerView则是通过复用机制,避免一次性加载所有的item。

解决方案:

1:让RecyclerView适配ScrollView滑动。

  1. 设置RecyclerView的setNestedScrollingEnabled这个属性为false。不让ReclclerView自己滚动,都交给ScrollView来处理滚动事件,但是这样的话会让RecyclerView失去自己的优势。 recyclerView.isNestedScrollingEnabled = false
  2. 动态计算RecyclerView的高度,由于RecyclerView默认是无限滚动的,当放入ScrollView后不会撑满, 通过动态计算RecyclerView的高度设置FullyExpandedLinearLayoutManager让RecyclerView展开,避免滑动冲突。
kotlin 复制代码
class FullyExpandedLinearLayoutManager(context: Context) : LinearLayoutManager(context) {
    override fun onMeasure(recycler: RecyclerView.Recycler, state: RecyclerView.State, widthSpec: Int, heightSpec: Int) {
        val expandedHeightSpec = MeasureSpec.makeMeasureSpec(Int.MAX_VALUE shr 2, MeasureSpec.AT_MOST)
        super.onMeasure(recycler, state, widthSpec, expandedHeightSpec)
    }
}

然后在 RecyclerView 中使用这个 LayoutManager

recyclerView.layoutManager = FullyExpandedLinearLayoutManager(context)

作用:

RecyclerView 适应 ScrollView,动态测量 RecyclerView 高度,使其完全展开,避免滑动冲突。

总结:这个情况只适用于数据量比较小,不需要分页加载希望整个页面滑动的时候使用。

2:只保留RecyclerView,去掉ScrollView

自定义Adapter处理RecycleView内容

kotlin 复制代码
class HeaderFooterAdapter(private val context: Context, private val data: List<String>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    
    private val TYPE_HEADER = 0
    private val TYPE_ITEM = 1

    override fun getItemViewType(position: Int): Int {
        return if (position == 0) TYPE_HEADER else TYPE_ITEM
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return if (viewType == TYPE_HEADER) {
            val view = LayoutInflater.from(context).inflate(R.layout.header_layout, parent, false)
            HeaderViewHolder(view)
        } else {
            val view = LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false)
            ItemViewHolder(view)
        }
    }

    override fun getItemCount(): Int = data.size + 1  // 1 for header

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is ItemViewHolder) {
            holder.textView.text = data[position - 1]
        }
    }

    class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view)
    class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView = view.findViewById(R.id.item_text)
    }
}

RecyclerView 处理整个页面的滚动,避免多层嵌套引起的性能问题 直接用 RecyclerView 处理所有的内容,包括 Header ,避免 ScrollView 的嵌套,提升流畅度

RecyclerView和ViewPager2嵌套(ViewPager2和ViewPager2嵌套也适用)

原因:

由于ViewPager2内部依然是使用RecyclerView实现的,因此两者嵌套使用会出现滑动冲突问题。

解决方案:

自定一个Layout继承基础Layout控件,通过重写onInterceptTouchEvent方法,在action_down事件中默认让子控件处理滑动事件,在ACTION_MOVE事件中去判断子控件是否滑动到了最左侧或最右侧,如果是的话,将事件交给父控件处理。将这个自定义控件包裹住子控件。

kotlin 复制代码
class InterceptLayout@JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    private var startX = 0f
    private var startY = 0f
    private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        val recyclerView = getChildAt(0) as? RecyclerView ?: return super.onInterceptTouchEvent(ev)

        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                startX = ev.x
                startY = ev.y
                parent.requestDisallowInterceptTouchEvent(true) // 默认让 RecyclerView 处理滑动
            }

            MotionEvent.ACTION_MOVE -> {
                val dx = ev.x - startX
                val dy = ev.y - startY

                if (abs(dx) > abs(dy) && abs(dx) > touchSlop) {
                    if (dx > 0 && !recyclerView.canScrollHorizontally(-1)) {
                        // ➡️ 向右滑,RecyclerView 已经滚动到最左侧,交给 ViewPager2 处理
                        parent.requestDisallowInterceptTouchEvent(false)
                        return false
                    } else if (dx < 0 && !recyclerView.canScrollHorizontally(1)) {
                        // ⬅️ 向左滑,RecyclerView 已经滚动到最右侧,交给 ViewPager2 处理
                        parent.requestDisallowInterceptTouchEvent(false)
                        return false
                    }
                }
            }
        }
        return super.onInterceptTouchEvent(ev)
    }
}

RecyclerView 嵌套 SwipeRefreshLayout

原因:

SwipeRefreshLayout的下拉刷新可能会与RecyclerView滑动冲突

解决方案:

自定义SwipeRefreshLayout控件,重写canChildScrollUp方法

kotlin 复制代码
class FixedSwipeRefreshLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {

    var targetRecyclerView: RecyclerView? = null

    override fun canChildScrollUp(): Boolean {
        return targetRecyclerView?.canScrollVertically(-1) ?: super.canChildScrollUp()
    }
}

在代码中设置: swipeRefreshLayout.targetRecyclerView = recyclerView

就先这样,只想到这么多了。。。。。。。。。。。。

相关推荐
志存高远6613 分钟前
kotlin 扩展函数
android·开发语言·kotlin
小镇敲码人2 小时前
【深入浅出MySQL】之数据类型介绍
android·数据库·mysql
柯南二号3 小时前
Android 实现一个隐私弹窗
android
UzumakiHan5 小时前
flutter权限允许访问
android·flutter
wangz767 小时前
kotlin、jetpack compose、Android加速度传感器调用
android·kotlin·jetpack compose·加速度传感器
东坡大表哥7 小时前
【Android】Android签名解析
android·java
每次的天空7 小时前
Android学习总结之GetX库篇(场景运用)
android·javascript·学习
Ya-Jun9 小时前
性能优化实践:渲染性能优化
android·flutter·ios·性能优化
Hzhile10 小时前
攻防世界-php伪协议和文件包含
android·开发语言·安全·web安全·网络安全·php
追随远方11 小时前
详解 FFMPEG 交叉编译 `FLAGS` 和 `INCLUDES` 的作用
android·ffmpeg·myeclipse·音频编解码