滑动冲突

根本原因:

由于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】Room数据库的使用
android·数据库·room
lichong95113 分钟前
【混合开发】Android+Webview+VUE播放视频之视频解析工具mediainfo-Macos
android·macos·架构·vue·音视频·api·postman
翻滚丷大头鱼6 小时前
android View详解—View的刷新流程源码解析
android
zhangphil7 小时前
Android adb shell命令分析应用内存占用
android·adb
漠缠8 小时前
Android AI客户端开发(语音与大模型部署)面试题大全
android·人工智能
Lei活在当下9 小时前
一个基础问题:关于SDK初始化时机的选择
android
雨白11 小时前
Android 触摸反馈与事件分发原理解析
android
relis13 小时前
解密大语言模型推理:Prompt Processing 的内存管理与计算优化
android·语言模型·prompt
CYRUS STUDIO16 小时前
FART 自动化脱壳框架优化实战:Bug 修复与代码改进记录
android·自动化·逆向·fart
2501_9159090616 小时前
uni-app iOS 上架常见问题与解决方案,实战经验全解析
android·ios·小程序·https·uni-app·iphone·webview