滑动冲突

根本原因:

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

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

相关推荐
Libraeking12 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位12 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen12314 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs15 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob15 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔15 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei99615 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly17 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首18 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-194320 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed