Android 运动布局不相应
Android motion layout not working accordingly
我是 motionlayout
的新手,一直在关注 this 等各种在线教程以了解其工作原理。简而言之,我已经知道它基本上是动画 constraintSets
,你有一个 start
和 end
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不响应我的swipes
和addressViews
在初始状态下消失了?
我终于设法使 MotionLayout
和 CoordinatorLayout
都能正常工作。我只会 post Coordinator
解决方案,因为它很长而且我没有时间,如果有人需要它,请发表评论,我会 post.
我创建了 3 个布局,1. The main layout with the map
、2. Top bar with the to and from address EditTexts
和 3. The bottom layout
向上滑动并显示顶部栏。
解决方案 1 使用 CoordinatorLayout
顶栏
<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>
像底部一样的底部布局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>
最后是我的地图布局中包含的两个布局
<?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
下方
我是 motionlayout
的新手,一直在关注 this 等各种在线教程以了解其工作原理。简而言之,我已经知道它基本上是动画 constraintSets
,你有一个 start
和 end
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不响应我的swipes
和addressViews
在初始状态下消失了?
我终于设法使 MotionLayout
和 CoordinatorLayout
都能正常工作。我只会 post Coordinator
解决方案,因为它很长而且我没有时间,如果有人需要它,请发表评论,我会 post.
我创建了 3 个布局,1. The main layout with the map
、2. Top bar with the to and from address EditTexts
和 3. The bottom layout
向上滑动并显示顶部栏。
解决方案 1 使用 CoordinatorLayout
顶栏
<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>
像底部一样的底部布局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>
最后是我的地图布局中包含的两个布局
<?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