将水平RecyclerView嵌套NestedScrollView后MotionLayout进度跳变求助
MotionLayout进度从0跳至1的问题排查与解决
刚接触MotionLayout,布局里包含两个RecyclerView,一个水平方向、一个垂直方向。之前垂直滚动时水平RecyclerView的动画不生效,把水平RecyclerView放进NestedScrollView后动画正常了,但现在MotionLayout的进度直接从0跳至1,没有过渡动画。
布局代码(Fragment)
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/motionLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/fragment_lista_principal_scene" tools:context=".framework.ui.main.fragments.FragmentListaPrincipal"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/linearADesaparecer" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <androidx.core.widget.NestedScrollView android:id="@+id/scroll" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraint" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/imagen" android:layout_width="match_parent" android:layout_height="150dp" android:background="@color/black" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvPromociones" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:visibility="visible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/imagen" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> </androidx.constraintlayout.widget.ConstraintLayout> <HorizontalScrollView android:id="@+id/menuRapido" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:visibility="invisible" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="@id/linearADesaparecer"> <RadioGroup android:id="@+id/rGroup" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <RadioButton android:id="@+id/rbSugerencias" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Sugerencias" android:textColor="@color/black" android:textSize="16sp" /> <RadioButton android:id="@+id/rbMeriendas" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Meriendas" android:textColor="@color/black" android:textSize="16sp" /> <RadioButton android:id="@+id/rbMinutas" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Minutas y tablas" android:textColor="@color/black" android:textSize="16sp" /> <RadioButton android:id="@+id/rbPostres" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Postres" android:textColor="@color/black" android:textSize="16sp" /> <RadioButton android:id="@+id/rbCervezas" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Cervezas" android:textColor="@color/black" android:textSize="16sp" /> <RadioButton android:id="@+id/rbTragos" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_min" android:background="@drawable/radio_selector" android:button="@android:color/transparent" android:elevation="4dp" android:fontFamily="@font/ubunturegular" android:padding="@dimen/common_padding_min" android:text="Tragos y vinos" android:textColor="@color/black" android:textSize="16sp" /> </RadioGroup> </HorizontalScrollView> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rvProductos" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/menuRapido" tools:listitem="@layout/item_producto" /> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/constraintLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <com.facebook.shimmer.ShimmerFrameLayout android:id="@+id/shimmerProductos" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:visibility="invisible" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/item_producto_load" /> <include layout="@layout/item_producto_load" /> <include layout="@layout/item_producto_load" /> <include layout="@layout/item_producto_load" /> <include layout="@layout/item_producto_load" /> <include layout="@layout/item_producto_load" /> </LinearLayout> </com.facebook.shimmer.ShimmerFrameLayout> </androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.button.MaterialButton android:id="@+id/btnVerCarrito" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="@dimen/common_padding_default" android:backgroundTint="@color/black" android:text="ver carrito" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.motion.widget.MotionLayout>
MotionScene代码
<?xml version="1.0" encoding="utf-8"?> <MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <Transition motion:constraintSetEnd="@+id/end" motion:constraintSetStart="@id/start" motion:duration="1000"> <KeyFrameSet> </KeyFrameSet> <OnSwipe motion:dragDirection="dragDown" motion:touchAnchorSide="bottom" /> </Transition> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/linearADesaparecer" android:layout_width="match_parent" android:layout_height="wrap_content" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@+id/menuRapido" motion:layout_constraintEnd_toEndOf="parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="invisible" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintBottom_toBottomOf="@id/linearADesaparecer" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/linearADesaparecer" android:layout_width="match_parent" android:layout_height="wrap_content" motion:layout_constraintBottom_toTopOf="parent" /> <Constraint android:id="@+id/menuRapido" motion:layout_constraintEnd_toEndOf="parent" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="visible" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintTop_toTopOf="parent" /> <Constraint android:id="@+id/rvProductos" android:layout_width="match_parent" android:layout_height="0dp" motion:layout_constraintTop_toBottomOf="@id/menuRapido" motion:layout_constraintBottom_toBottomOf="parent" /> </ConstraintSet> </MotionScene>
问题原因与解决方法
核心原因
- NestedScrollView抢占触摸事件:NestedScrollView设置为
match_parent后,会覆盖顶部整个区域,优先处理垂直滑动事件,导致MotionLayout无法持续接收滑动增量,只能触发一次性状态切换。 - 滑动锚点配置不合理:当前
OnSwipe未指定明确的触摸锚点View,滑动事件被子View拦截后,MotionLayout无法正确计算进度。
具体修复步骤
1. 调整NestedScrollView高度
将NestedScrollView的android:layout_height="match_parent"改为android:layout_height="wrap_content",避免它撑满父布局抢占所有滑动事件:
<androidx.core.widget.NestedScrollView android:id="@+id/scroll" android:layout_width="match_parent" android:layout_height="wrap_content">
2. 让MotionLayout优先接收触摸事件
在MotionLayout根节点添加属性,阻止子View抢占触摸事件:
<androidx.constraintlayout.motion.widget.MotionLayout ... android:descendantFocusability="blocksDescendants">
3. 优化滑动锚点配置
修改MotionScene的OnSwipe,指定垂直RecyclerView为触摸锚点,确保滑动事件被MotionLayout正确捕获:
<OnSwipe motion:dragDirection="dragDown" motion:touchAnchorId="@+id/rvProductos" motion:touchAnchorSide="top" motion:maxDragPercent="0.3"/>
4. 简化布局层级
移除冗余的linearADesaparecer约束布局,直接将NestedScrollView作为MotionLayout的子View,减少约束冲突概率,同时同步修改MotionScene中对应的ID引用。
5. 开启RecyclerView嵌套滑动支持
给垂直RecyclerView添加嵌套滑动属性,确保它和MotionLayout配合处理滑动:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/rvProductos" ... android:nestedScrollingEnabled="true"/>
内容的提问来源于stack exchange,提问作者Gregorio Scalbi
相关产品推荐
相关产品推荐

