当前位置: 首页 > news >正文

Android 中解决 RecyclerView 和子控件之间的滑动冲突问题

一、简述滑动冲突问题

在 Android 开发中,RecyclerView 和其子控件之间的滑动冲突是一个常见的问题。这种冲突通常发生在 RecyclerView 的子项本身也支持滑动操作时,例如子项是一个 ProgressBar、WebView 或其他自定义的滑动视图。当用户在子控件上滑动时,可能会触发 RecyclerView 的滑动,从而导致子控件滑动异常。

二、解决方案

  • requestDisallowInterceptTouchEvent 是 Android 中一个非常重要的方法,用于解决嵌套滑动(Nested Scrolling)或滑动冲突问题。它允许子视图(如 ScrollView、WebView 等)告诉父视图(如 RecyclerView、ViewPager 等)不要拦截当前的触摸事件,从而确保子视图能够正确处理这些事件。
  • 当调用 requestDisallowInterceptTouchEvent(true) 时,父视图的 onInterceptTouchEvent 方法将不会拦截当前的触摸事件。这意味着触摸事件会直接传递给子视图,而不是被父视图处理。

三、示例(RecyclerView 和 子控件 ProgressBar)

假设你有一个 RecyclerView,其中的每个子项包含一个可滑动的 ProgressBar 横向进度条。如果没有调用 requestDisallowInterceptTouchEvent(true),当用户在 ProgressBar 上滑动时,RecyclerView 可能会拦截这些事件,导致 ProgressBar 无法正常滑动。通过在 ProgressBar 的 onTouchEvent 中调用 requestDisallowInterceptTouchEvent(true),可以确保 ProgressBar 能够正常处理滑动事件。

1、子项布局文件 item_recycleview.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"android:padding="20dp"><ProgressBarandroid:id="@+id/progress_bar"android:layout_width="100dp"android:layout_height="match_parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toBottomOf="parent"style="?android:attr/progressBarStyleHorizontal"android:max="100"android:progress="50"/></androidx.constraintlayout.widget.ConstraintLayout>

2、适配器类 CustomAdapter.kt

在子控件的 onTouchEvent 方法中根据触摸事件类型来调用 requestDisallowInterceptTouchEvent 方法:

  • 在触摸事件开始时(MotionEvent.ACTION_DOWN),调用 requestDisallowInterceptTouchEvent(true),通知父控件不要拦截事件。
  • 在触摸事件结束时( MotionEvent.ACTION_UP 或 MotionEvent.ACTION_CANCEL),调用 requestDisallowInterceptTouchEvent(false),允许父控件重新拦截触摸事件。
package com.example.helloworldimport android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
import androidx.recyclerview.widget.RecyclerViewclass CustomAdapter: RecyclerView.Adapter<CustomAdapter.ViewHolder>() {@SuppressLint("ClickableViewAccessibility")class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {private val progressBar: ProgressBar = itemView.findViewById(R.id.progress_bar)init {var lastX = 0fprogressBar.setOnTouchListener { view, motionEvent ->when (motionEvent.action) {MotionEvent.ACTION_DOWN -> {// 当触摸事件发生时,通知父视图不要拦截触摸事件progressBar.parent.requestDisallowInterceptTouchEvent(true)}MotionEvent.ACTION_UP -> {// 当触摸事件发生时,通知父视图可以拦截触摸事件progressBar.parent.requestDisallowInterceptTouchEvent(false)}MotionEvent.ACTION_MOVE -> {if (motionEvent.x > lastX) {progressBar.progress += 2} else {progressBar.progress -= 2}lastX = motionEvent.x}}true}}}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recycleview, parent, false)return ViewHolder(view)}override fun getItemCount(): Int {return 5}override fun onBindViewHolder(holder: ViewHolder, position: Int) {}
}

3、Activity 的布局文件 activity_scroll_conflict.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycler_view"android:layout_width="300dp"android:layout_height="100dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"android:layout_marginTop="20dp"android:layout_marginStart="20dp"/></androidx.constraintlayout.widget.ConstraintLayout>

4、ScrollConflictActivity.kt 文件

package com.example.helloworldimport android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.helloworld.databinding.ActivityScrollConflictBindingclass ScrollConflictActivity: AppCompatActivity() {private lateinit var _binding: ActivityScrollConflictBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)_binding = ActivityScrollConflictBinding.inflate(layoutInflater)setContentView(_binding.root)_binding.recyclerView.adapter = CustomAdapter()_binding.recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)}
}

四、其他注意事项

  • 性能考虑:频繁调用 requestDisallowInterceptTouchEvent 可能会影响性能,尤其是在复杂的布局中,建议仅在必要时调用此方法。
  • 嵌套滚动支持:如果子控件支持嵌套滚动(如 NestedScrollView),可以使用 NestedScrollingParent 和 NestedScrollingChild 接口来实现更复杂的嵌套滚动逻辑。

相关文章:

  • 文档构建:Sphinx全面使用指南 — 进阶篇
  • Android TV 输入框架(TIF)深度解析与实践指南
  • 【Java学习日记25】:带返回值的方法
  • 【6D位姿估计】Foundation Pose复现
  • 【LangChain4j】AI 第二弹:项目中接入 LangChain4j
  • 从入门到精通:CMakeLists.txt 完全指南
  • 【MQ篇】RabbitMQ之工作队列模式!
  • 【无标题】spark安装部署
  • 16.第二阶段x64游戏实战-分析二叉树结构
  • CAMAT
  • FreeRTOS深度解析:队列集(Queue Sets)的原理与应用
  • 域名 → IP 的解析全过程
  • 【PCB工艺】推挽电路及交越失真
  • 厚铜PCB制造中的散热结构工艺控制要点
  • 探秘Transformer系列之(30)--- 投机解码
  • JavaScript 改变this指向
  • LeetCode第164题_最大间距
  • 图文结合 - 光伏系统产品设计PRD文档 -(慧哥)慧知开源充电桩平台
  • 前端 JavaScript 处理流式响应的坑
  • DeepSeek+Mermaid:轻松实现可视化图表自动化生成(附实战演练)
  • 用一生走丝路,91岁艺术家耿玉琨的书旅奇遇
  • 吉林省委原书记、吉林省人大常委会原主任何竹康逝世
  • 浙江严禁中小学节假日集体补课,省市县教育部门公布举报电话
  • 特斯拉季度利润暴跌71%,马斯克下月开始大幅减少为政府工作时间
  • 华天酒店:2024年归母净亏损约1.81亿元,已连续亏损3年
  • 十二届上海市委第六轮巡视启动,对18家市管单位开展常规巡视