滑动冲突

根本原因:

由于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

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

相关推荐
_一条咸鱼_2 小时前
Android Fresco 框架工具与测试模块源码深度剖析(五)
android
QING6182 小时前
Android Jetpack Security 使用入门指南
android·安全·android jetpack
顾林海2 小时前
Jetpack LiveData 使用与原理解析
android·android jetpack
七郎的小院2 小时前
性能优化ANR系列之-BroadCastReceiver ANR原理
android·性能优化·客户端
QING6182 小时前
Android Jetpack WorkManager 详解
android·kotlin·android jetpack
今阳3 小时前
鸿蒙开发笔记-14-应用上下文Context
android·华为·harmonyos
东莞梦幻科技4 小时前
体育直播系统趣猜功能开发技术实现方案
android
姜行运4 小时前
数据结构入门【算法复杂度】
android·c语言·数据结构·算法
二流小码农4 小时前
鸿蒙开发:父组件如何调用子组件中的方法?
android·ios·harmonyos
张风捷特烈5 小时前
Flutter 知识集锦 | 获取函数调用栈
android·flutter·dart