Android 运动布局不相应

Android motion layout not working accordingly

我是 motionlayout 的新手,一直在关注 this 等各种在线教程以了解其工作原理。简而言之,我已经知道它基本上是动画 constraintSets,你有一个 startend constraintSet,你可以用 KeyFrameSets 进一步自定义。我有这个布局

我要模仿Lyft's bottom sheet

在我的布局中,Where are you going 按钮应该会随着搜索目标 textInputs 淡入而慢慢淡出。底部的 recyclerview 应该保存已保存 addresses, 不受影响。我使用标准 bottomsheet 尝试了此实现,但在动画方面遇到了挑战,它有这种奇怪的 flickering,所以我决定使用具有普通视图的 MotionLayout

我的bottomsheet布局如下

<com.google.android.material.card.MaterialCardView 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/cardChooseAddressBottomSheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    app:shapeAppearance="@style/ShapeAppearanceRoundedLargeTopCorners">



    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/bottomSheetConstraintLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">



        <ImageView
            android:id="@+id/swipeUpHandle"
            android:layout_width="50dp"
            android:layout_height="30dp"
            android:layout_gravity="center"
            android:background="@drawable/ic_swipe_up_handle"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />



        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/hiThere"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/medium_margin"
            android:text="@string/hi_there"
            android:textAppearance="@style/h6_headline"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/swipeUpHandle"
            />


        <com.google.android.material.button.MaterialButton
            android:id="@+id/btnSearch"
            style="@style/Widget.MaterialComponents.Button.OutlinedButton"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_marginTop="@dimen/medium_margin"
            android:gravity="start|center_vertical"
            android:letterSpacing="0.0"
            android:text="@string/where_are_you_going"
            android:textAllCaps="false"
            android:textAppearance="@style/subtitle1"
            android:textColor="@android:color/darker_gray"
            app:backgroundTint="@android:color/white"
            app:icon="@drawable/ic_search"
            app:iconTint="@android:color/black"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/hiThere"
            app:shapeAppearanceOverlay="@style/ShapeAppearanceRoundedMediumAllCorners" />



        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:id="@+id/addressViews"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btnSearch">



            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/inputOrigin"
                style="@style/textInput"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/medium_margin"
                android:hint="@string/search_destination"
                android:textColorHint="@android:color/darker_gray"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/edtOrigin"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:textAppearance="@style/subtitle1"
                    android:textColor="@android:color/white" />

            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/inputDestination"
                style="@style/textInput"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/medium_margin"
                android:hint="@string/search_destination"
                android:textColorHint="@android:color/darker_gray"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/inputOrigin">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/edtDestination"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:textAppearance="@style/subtitle1"
                    android:textColor="@android:color/white" />

            </com.google.android.material.textfield.TextInputLayout>


        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:id="@+id/recyclerAddresses"
            android:layout_marginTop="@dimen/medium_margin"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/addressViews"
            tools:listitem="@layout/recycler_view_item" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</com.google.android.material.card.MaterialCardView>

我包含 bottomsheet 的父布局如下

<?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"
    app:layoutDescription="@xml/taxi_bottomsheet_scene"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />



    <include
        layout="@layout/choose_destination_bottom_sheet_layout"/>

</androidx.constraintlayout.motion.widget.MotionLayout>

最后我的 taxi_bottomsheet_scene 动作场景是

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">



    <Transition
        app:constraintSetEnd="@+id/expanded"
        app:constraintSetStart="@+id/collapsed"
        app:duration="1000">

        <OnSwipe
            app:touchAnchorId="@+id/btnSearch"
            app:touchAnchorSide="top"
            app:dragDirection="dragUp"/>


    </Transition>


    <ConstraintSet android:id="@+id/expanded">

        <Constraint
            android:id="@+id/cardChooseAddressBottomSheet"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHeight_percent="1"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0" />



        <Constraint
            android:id="@+id/addressViews"
            app:layout_constraintHeight_percent="1"/>


        <Constraint
            android:id="@+id/btnSearch"
            app:layout_constraintHeight_percent="0"/>

    </ConstraintSet>




    <ConstraintSet android:id="@+id/collapsed">

        <Constraint
            android:id="@+id/cardChooseAddressBottomSheet"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHeight_percent="0.4"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="1.0"  />


        <Constraint
            android:id="@+id/addressViews"
            app:layout_constraintHeight_percent="0.0"/>



        <Constraint
            android:id="@+id/btnSearch"
            app:layout_constraintHeight_percent="0.0"/>
    </ConstraintSet>





</MotionScene>

当我启动这个应用程序时,我无法让底部sheet向上滑动,它根本没有任何反应。我注意到的一件事是在添加 app:layoutDescription="@xml/taxi_bottomsheet_scene" 属性后,底部 sheet 大小更改为我在 constraintSetStart 中指定的大小,但 addressViews 视图没有。

所以我的布局看起来像

所以我的问题是,我的屁股sheet不响应我的swipesaddressViews在初始状态下消失了?

我终于设法使 MotionLayoutCoordinatorLayout 都能正常工作。我只会 post Coordinator 解决方案,因为它很长而且我没有时间,如果有人需要它,请发表评论,我会 post.

我创建了 3 个布局,1. The main layout with the map2. Top bar with the to and from address EditTexts3. The bottom layout 向上滑动并显示顶部栏。

解决方案 1 使用 CoordinatorLayout

  1. 顶栏

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:layout_marginBottom="@dimen/medium_margin"
        app:layout_scrollFlags="noScroll">
    
    
        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/topToolBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navigationIcon="@drawable/ic_arrow_back_black_24dp" />
    
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/addressViews"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginStart="@dimen/activity_horizontal_margin"
            android:layout_marginEnd="@dimen/activity_horizontal_margin"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/topToolBar">
    
    
            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/imgOrigin"
                android:layout_width="@dimen/activity_horizontal_margin"
                android:layout_height="@dimen/activity_horizontal_margin"
                android:layout_marginStart="@dimen/medium_margin"
                android:layout_marginEnd="@dimen/medium_margin"
                app:layout_constraintBottom_toBottomOf="@+id/inputOrigin"
                app:layout_constraintEnd_toStartOf="@+id/inputOrigin"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="@+id/inputOrigin"
                app:srcCompat="@drawable/ic_origin"
                app:tint="@color/colorAccent" />
    
    
            <View
                android:layout_width="2dp"
                android:layout_height="0dp"
                android:layout_marginTop="@dimen/xsmall_margin"
                android:layout_marginBottom="@dimen/xsmall_margin"
                android:background="@drawable/accent_to_color_primary_dark__negative_90_gradient"
                app:layout_constraintBottom_toTopOf="@+id/imgDestination"
                app:layout_constraintEnd_toEndOf="@+id/imgOrigin"
                app:layout_constraintStart_toStartOf="@+id/imgOrigin"
                app:layout_constraintTop_toBottomOf="@+id/imgOrigin" />
    
            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/inputOrigin"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/medium_margin"
                android:hint="@string/pick_up_location"
                android:marqueeRepeatLimit="marquee_forever"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/imgOrigin"
                app:layout_constraintTop_toTopOf="parent">
    
                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/edtOrigin"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    android:inputType="textPostalAddress"
                    android:singleLine="true"
                    android:textAppearance="@style/subtitle1" />
    
            </com.google.android.material.textfield.TextInputLayout>
    
    
            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/imgDestination"
                android:layout_width="@dimen/activity_horizontal_margin"
                android:layout_height="@dimen/activity_horizontal_margin"
                android:layout_marginStart="@dimen/medium_margin"
                android:layout_marginEnd="@dimen/medium_margin"
                app:layout_constraintBottom_toBottomOf="@+id/inputDestination"
                app:layout_constraintEnd_toStartOf="@+id/inputDestination"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="@+id/inputDestination"
                app:srcCompat="@drawable/ic_destination"
                app:tint="@color/colorPrimaryDark" />
    
            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/inputDestination"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/medium_margin"
                android:hint="@string/search_destination"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@+id/imgDestination"
                app:layout_constraintTop_toBottomOf="@id/inputOrigin">
    
                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/edtDestination"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/white"
                    android:inputType="textPostalAddress"
                    android:singleLine="true"
                    android:textAppearance="@style/subtitle1" />
    
            </com.google.android.material.textfield.TextInputLayout>
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    </com.google.android.material.appbar.AppBarLayout>
    
  2. 像底部一样的底部布局sheet

    <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cardChooseAddressBottomSheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:cardBackgroundColor="@color/white"
    app:behavior_hideable="false"
    app:layout_behavior="@string/bottom_sheet_behavior">
    
    
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
    
    
    
        <ImageView
            android:id="@+id/swipeUpHandle"
            android:layout_width="35dp"
            android:layout_height="30dp"
            android:layout_gravity="center"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:background="@drawable/ic_swipe_up_handle"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    
        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/hiThere"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hi_there"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:textAppearance="@style/h6_headline"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/swipeUpHandle" />
    
    
        <com.google.android.material.button.MaterialButton
            android:id="@+id/btnSearch"
            style="@style/Widget.MaterialComponents.Button.OutlinedButton"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_marginLeft="@dimen/activity_horizontal_margin"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:gravity="start|center_vertical"
            android:letterSpacing="0.0"
            android:text="@string/where_are_you_going"
            android:textAllCaps="false"
            android:textAppearance="@style/subtitle1"
            android:textColor="@android:color/darker_gray"
            app:backgroundTint="@android:color/white"
            app:icon="@drawable/ic_search"
            app:iconSize="@dimen/medium_icon"
            app:iconTint="@color/colorAccent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/hiThere"
            app:shapeAppearanceOverlay="@style/ShapeAppearanceRoundedMediumAllCorners" />
    
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerAddresses"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/btnSearch" />
    
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerSearchAddresses"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="@color/white"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/btnSearch" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    </com.google.android.material.card.MaterialCardView>
    
  3. 最后是我的地图布局中包含的两个布局

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:id="@+id/coordinator"
        android:layout_height="match_parent">
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
    
            <fragment
                android:id="@+id/map"
                android:name="com.google.android.gms.maps.SupportMapFragment"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHeight_percent="0.67"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    
            <androidx.appcompat.widget.AppCompatImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/imgMapCenter"
                android:layout_marginBottom="@dimen/xlarge_margin"
                android:visibility="invisible"
                android:tint="@color/colorAccent"
                app:layout_constraintBottom_toBottomOf="@id/map"
                app:layout_constraintEnd_toEndOf="@id/map"
                app:layout_constraintStart_toStartOf="@id/map"
                app:layout_constraintTop_toTopOf="@id/map"
                app:srcCompat="@drawable/ic_destination" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    
    
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fabMyLocation"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:backgroundTint="@color/white"
            app:fabSize="mini"
            app:layout_anchor="@id/cardChooseAddressBottomSheet"
            app:layout_anchorGravity="top|right"
            app:srcCompat="@drawable/ic_origin"
            app:tint="@color/colorAccent" />
        <include layout="@layout/taxi_fragment_set_destination_top_bar" />
    
        <include layout="@layout/taxi_fragment_bottom_sheet_addresses_layout" />
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    

还有我的片段

   final private BottomSheetBehavior.BottomSheetCallback addressBottomSheetCallBack = new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {
            switch (newState) {
                case BottomSheetBehavior.STATE_HIDDEN:
                    break;

                case BottomSheetBehavior.STATE_SETTLING:
                    break;

                case BottomSheetBehavior.STATE_EXPANDED:
                    if (!allPermissionsGranted())
                        requestForLocationPermissions();
                    topAddressBar.setVisibility(Visibility.VISIBLE);
                     fabMyLocation.hide();
                    break;

                case BottomSheetBehavior.STATE_COLLAPSED:
                    if (!allPermissionsGranted())
                        requestForLocationPermissions();
                    topAddressBar.setVisibility(Visibility.INVISIBLE);
                    fabMyLocation.show();
                    break;

                case BottomSheetBehavior.STATE_DRAGGING:
                    break;

                case BottomSheetBehavior.STATE_HALF_EXPANDED:
                    break;
            }
        }



   BottomSheetBehavior addressBottomSheetBehavior = BottomSheetBehavior.from(cardChooseAddressBottomSheet);

      topAddressBar.post(() -> {
            CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) cardChooseAddressBottomSheet.getLayoutParams();
            layoutParams.height = ((Resources.getSystem().getDisplayMetrics().heightPixels + (int) utils.percentageOf(62, btnSearch.getMeasuredHeight())) - topAddressBar.getMeasuredHeight());
        });


        addressBottomSheetBehavior.setPeekHeight((int) utils.percentageOf(29, Resources.getSystem().getDisplayMetrics().heightPixels), true);
        addressBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);


        topToolBar.setNavigationOnClickListener(view12 -> {
            if (addressBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                utopAddressBar.setVisibility(INVISIBLE);
                addressBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            } else
                Navigation.findNavController(view12).navigateUp();
        });

        addressBottomSheetBehavior.addBottomSheetCallback(addressBottomSheetCallBack);

请注意我在 topAddressBar 上使用 INVISIBLE 而不是 GONE?那是因为每次我调用 GONE 时,根据我的假设,布局在理想情况下会 recalculate 并且地图会闪烁,以阻止我不得不使用 invisible 因为布局不会缩小,而是它仍然占用相同 space 但只是不可见。

另请注意,我正在添加填充 cardChooseAddressBottomSheet.getLayoutParams(),这是因为我需要 Sheet 不要在 topAddressBar 下方太深,以免隐藏我的 recyclerview 内容。当前填充确保 recyclerview 完全可见,并且它上面的所有其他内容都在 topAddressBar

下方