添加setOnClickListener后Motion Layout手动下拉滑动失效求助
问题:MotionLayout视图添加点击事件后滑动功能失效
我是MotionLayout新手,想实现一个类似YouTube视频底部布局的效果:视图可手动下拉至1dp消失,同时点击该视图能跳转至另一个Activity。但给视图添加setOnClickListener后,手动下拉滑动功能就失效了,试了很多教程都没解决。
添加点击事件前的效果:
添加点击事件后的效果:
相关代码片段
主布局(MotionLayout)
<androidx.constraintlayout.motion.widget.MotionLayout android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/activity_main_xml_constraintlayout_scene"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" android:nestedScrollingEnabled="false" app:layout_constrainedHeight="true" android:scrollbars="vertical" app:layout_constraintBottom_toTopOf="@id/motionLayoutExample" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:itemCount="20" tools:listitem="@layout/row_rv" /> <include android:id="@+id/motionLayoutExample" layout="@layout/view_motion" android:layout_width="match_parent" android:layout_height="65dp" app:layout_constraintBottom_toTopOf="@id/bottomNavView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"/> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottomNavView" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:menu="@menu/btn_nav" /> </androidx.constraintlayout.motion.widget.MotionLayout>
注:include标签对应的view_motion布局是需要实现下拉消失和点击跳转的视图区域
Motion Scene配置
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition android:id="@+id/motionTransitions" motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="1000"> <KeyFrameSet> <KeyAttribute motion:motionTarget="@+id/motionLayoutExample" motion:framePosition="100" android:alpha="0" /> <KeyAttribute motion:motionTarget="@+id/motionLayoutExample" motion:framePosition="75" android:alpha="0.5" /> </KeyFrameSet> <OnSwipe motion:touchAnchorId="@+id/motionLayoutExample" motion:dragDirection="dragDown" motion:touchAnchorSide="middle" motion:moveWhenScrollAtTop="false" motion:nestedScrollFlags="disableScroll" motion:onTouchUp="decelerate" motion:touchRegionId="@id/motionLayoutExample"/> </Transition> <ConstraintSet android:id="@+id/start"> </ConstraintSet> <ConstraintSet android:id="@+id/end" android:alpha="0"> <Constraint android:id="@+id/motionLayoutExample" motion:layout_constraintEnd_toEndOf="parent" android:layout_width="match_parent" android:layout_height="1dp" motion:layout_constraintBottom_toTopOf="@id/bottomNavView" motion:layout_constraintTop_toBottomOf="@id/recyclerView" motion:layout_constraintStart_toStartOf="parent" /> </ConstraintSet> </MotionScene>
导致滑动失效的点击事件代码
motionLayoutExample.setOnClickListener() { Intent(this, SecondActivity::class.java).also() { startActivity(it) } }
解决方案
方法一:给view_motion内部视图设置点击事件
不要直接给include的根视图(motionLayoutExample)设置点击事件,而是打开view_motion.xml,在其内部的可点击子视图(比如整个布局的容器)上绑定点击事件。这样根视图的触摸事件会优先传递给MotionLayout处理滑动,内部视图单独响应点击。
示例view_motion.xml修改:
<!-- view_motion.xml --> <LinearLayout android:layout_width="match_parent" android:layout_height="65dp" android:background="#FF0000" android:orientation="vertical"> <!-- 内部可点击区域 --> <TextView android:id="@+id/tv_click_area" android:layout_width="match_parent" android:layout_height="match_parent" android:text="点击跳转" android:gravity="center"/> </LinearLayout>
对应代码中绑定点击事件:
findViewById<TextView>(R.id.tv_click_area).setOnClickListener { startActivity(Intent(this, SecondActivity::class.java)) }
方法二:自定义触摸逻辑区分滑动与点击
如果必须给include的根视图设置点击事件,可以通过自定义OnTouchListener,判断触摸动作是滑动还是点击:
var downX = 0f var downY = 0f motionLayoutExample.setOnTouchListener { _, event -> when(event.action) { MotionEvent.ACTION_DOWN -> { downX = event.x downY = event.y false // 允许MotionLayout处理按下事件 } MotionEvent.ACTION_UP -> { val deltaX = abs(event.x - downX) val deltaY = abs(event.y - downY) // 滑动距离小于阈值则判定为点击 if (deltaX < 10 && deltaY < 10) { startActivity(Intent(this, SecondActivity::class.java)) } false // 让MotionLayout处理后续事件 } else -> false } }
额外优化:调整MotionScene滑动阈值
在MotionScene的OnSwipe标签中添加motion:touchSlop="10dp",增大滑动识别的阈值,避免轻微滑动被误判为点击:
<OnSwipe motion:touchAnchorId="@+id/motionLayoutExample" motion:dragDirection="dragDown" motion:touchAnchorSide="middle" motion:moveWhenScrollAtTop="false" motion:nestedScrollFlags="disableScroll" motion:onTouchUp="decelerate" motion:touchRegionId="@id/motionLayoutExample" motion:touchSlop="10dp"/>
内容的提问来源于stack exchange,提问作者Pup In The Tree
相关产品推荐
相关产品推荐

