关系
- BottomSheet不是真正存在的类,而是一种称呼,表示该种控件类型,参照Google翻译,本文以下称之为“底页”,就是从屏幕底部弹出的工具条。与之对应是- BottomSheetBehavior的行为类,它需要附属某个控件使用。该行为性质包括:- 
- 可以从底部弹出
- 可以上下拖拽布局
- 可以单击淡黑色遮罩隐藏/关闭
 
- BottomSheetDialog继承- Dialog,是一种对话框,它是拥有- BottomSheetBehavior行为的对话框,从而实现从底部弹出和上下拉伸的效果。
- BottomSheetDialogFragment是包含- BottomSheetDialog的片段(- Fragment),所以它可以同时利用- Fragment的特点和- BottomSheet这一交互效果
BottomSheetBehavior
使用
在xml布局文件中与CoordinatorLayout配合使用。如下设置后,NestedScrollView具有BottomSheetBehavior的性质,且一开始就显示在布局中,初始的显示高度为peekHeight值。背景没有淡黑色遮罩,可以上下拖拽。
- 关键语句app:layout_behavior="@string/bottom_sheet_behavior"。
- NestedScrollView的子视图只能有一个。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | <?xml version="1.0" encoding="utf-8"?><android.support.design.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:layout_height="match_parent"
 android:fitsSystemWindows="true">
 
 <android.support.v4.widget.NestedScrollView
 android:id="@+id/bottom_sheet"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 app:behavior_hideable="true"
 app:behavior_peekHeight="50dp"
 app:layout_behavior="@string/bottom_sheet_behavior">
 
 
 
 </android.support.v4.widget.NestedScrollView>
 
 </android.support.design.widget.CoordinatorLayout>
 
 
 | 
上述代码中有两个属性值得注意。
app:behavior_hideable:当我们上下拖拽时,布局是否可以全部隐藏。如果设置为真,那么你向下滑完之后,布局会被隐藏起来,然后就滑不出来了。。。(除非你写了按钮之类的逻辑控制它的行为)所以要谨慎。
app:behavior_peekHeight:是当底页关闭的时候,底部我们能看到的高度,默认是0不可见。
获取行为
在布局文件中声明之后,就可以在代码中获取行为了。
| 12
 3
 4
 5
 
 | View = findViewById(R.id.bottom_sheet);
 
 BottomSheetBehavior behavior = BottomSheetBehavior.from(view);
 
 
 | 
方法
行为的一些常用的方法。
| 方法名 | 用法示例 | 说明 | 
| setHideable | setHideable(true) | 对应 app:behavior_hideable属性 | 
| setPeekHeight | setPeekHeight(500) | 对应 app:behavior_peekHeight属性 | 
| setBottomSheetCallback | / | 设置监听回调 | 
| setState | setState(BottomSheetBehavior.STATE_EXPANDED) | 设置底页状态 | 
BottomSheetCallback
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {@Override
 public void onStateChanged(@NonNull View bottomSheet, int newState)
 {
 
 }
 @Override
 public void onSlide(@NonNull View bottomSheet, float slideOffset)
 {
 
 }
 });
 
 
 | 
底页行为一共有五种状态。
- STATE_HIDDEN: 隐藏状态。默认是false,可通过- app:behavior_hideable属性设置。
 
- STATE_COLLAPSED: 折叠关闭状态。可通过- app:behavior_peekHeight来设置显示的高度,peekHeight默认是0。
 
- STATE_DRAGGING: 被拖拽状态
 
- STATE_SETTLING: 拖拽松开之后到达终点位置(collapsed or expanded)前的状态。
 
- STATE_EXPANDED: 完全展开的状态。
 
BottomSheetDialog
这是具有底页行为性质的对话框,不需要与CoordinatorLayout配合使用。弹出时,背景为出现一层淡黑色遮罩。需要相关逻辑控制它的弹出。
使用布局文件
设计BottomSheetDialog内部的视图。Xml可以不用被CoordinatorLayout包裹,但是还是推荐实用推荐的滑动控件NestedScrollView。
使用代码
使用上述的布局文件,假设名称为layout_bsd。可以获取行为实现自定义。要注意的是从视图的父视图((View)view.getParent())获取底页行为,否则会报错:
The view is not a child of CoordinatorLayout.
| 12
 3
 4
 5
 6
 
 | BottomSheetDialog dialog = new BottomSheetDialog(context);View view = getLayoutInflater.inflate(R.layout.layout_bsd, null);
 dialog.setContentView(view);
 
 BottomSheetBehavior behavior = BottomSheetBehavior.from((View)view.getParent());
 dialog.show();
 
 | 
BottomSheetDialogFragment
有两种用法。
- 当作中装了一个底页对话框的Fragment。实际是与底页对话框的作用和使用方法是相同的。
- 具有底页行为的Fragment。
用法1 披着 Fragment 外衣的 Dialog
重写BottomSheetDialogFragment中的onCreateDialog方法。可以看到和上面BottomSheetDialog的代码是相同的。同样可以在中间获取行为用于自定义。要注意是子视图的扩充用到的是View.inflate的静态方法,否则会报错。
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | @Overridepublic Dialog onCreateDialog(Bundle savedInstanceState)
 {
 BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
 View view = View.inflate(getContext(), R.layout.dialog_bottom_sheet, null);
 dialog.setContentView(view);
 mBehavior = BottomSheetBehavior.from((View) view.getParent());
 return dialog;
 }
 
 | 
用法2 Fragment
重写BottomSheetDialogFragment中的onCreateView方法。代码与Fragment的常规写法相同。不过不好的一点是不容易获取行为。如果要强行获取行为的话,可以使用以下的代码。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | @Overridepublic void onStart() {
 super.onStart();
 Dialog dialog = getDialog();
 
 if (dialog != null) {
 View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet);
 bottomSheet.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
 }
 View view = getView();
 view.post(() -> {
 View parent = (View) view.getParent();
 CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) (parent).getLayoutParams();
 CoordinatorLayout.Behavior behavior = params.getBehavior();
 BottomSheetBehavior bottomSheetBehavior = (BottomSheetBehavior) behavior;
 bottomSheetBehavior.setPeekHeight(view.getMeasuredHeight());
 ((View)bottomSheet.getParent()).setBackgroundColor(Color.TRANSPARENT);
 });
 }
 
 | 
但是如果这两个方法都重写了,那么以onCreateView里的视图为准,onCreateDialog会被覆盖。
扩展: 通过设置style实现圆角样式
如下设置好之后,在创建BottomSheetDialog时传入样式。
| 1
 | BottomSheetDialog dialog = new BottomSheetDialog(context, R.style.BottomSheetDialog);
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | <style name="BottomSheet" parent="Widget.Design.BottomSheet.Modal">
 
 <item name="android:background">@drawable/bg_bottom_sheet</item>
 </style>
 <style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
 <item name="android:windowIsFloating">false</item>
 
 <item name="bottomSheetStyle">@style/BottomSheet</item>
 
 <item name="android:statusBarColor">@color/transparent</item>
 
 <item name="android:navigationBarColor">@color/white</item>
 </style>
 
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle">
 <corners
 android:topLeftRadius="16dp"
 android:topRightRadius="16dp">
 </corners>
 <solid android:color="@android:color/white"/>
 <padding android:top="16dp"
 android:left="8dp"
 android:right="8dp" />
 </shape>
 
 
 | 
效果图
.png)
参考
BottomSheet、BottomSheetDialog使用详解
StackOverflow