滑动冲突

根本原因:

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

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

相关推荐
xiaguangbo1 小时前
rust slint android 安卓
android·linux·rust
lichong9511 小时前
【大前端++】Android studio Log日志高对比度配色方案
android·java·前端·json·android studio·大前端·大前端++
00后程序员张3 小时前
iOS 开发环境搭建完整指南 Xcode 安装配置、iOS 开发工具选择、ipa 打包与 App Store 上架实战经验
android·macos·ios·小程序·uni-app·iphone·xcode
顾林海3 小时前
揭秘Android编译插桩:ASM让你的代码"偷偷"变强
android·面试·性能优化
雨白3 小时前
初识协程: 为什么需要它以及如何启动第一个协程
android·kotlin
文阿花4 小时前
flutter 3.22+ Android集成高德Flutter地图自定义Marker显示
android·flutter
豆豆豆大王4 小时前
Android studio图像视图和相对布局知识点
android·ide·android studio
我命由我123455 小时前
Android 实例 - Android 圆形蒙版(Android 圆形蒙版实现、圆形蒙版解读)
android·java·java-ee·android studio·安卓·android-studio·android runtime
天若有情6736 小时前
【Android】Android项目目录结构及其作用
android
灿烂阳光g6 小时前
Android Automotive OS架构
android