关系
BottomSheet
不是真正存在的类,而是一种称呼,表示该种控件类型,参照Google翻译,本文以下称之为“底页”,就是从屏幕底部弹出的工具条。与之对应是BottomSheetBehavior
的行为类,它需要附属某个控件使用。该行为性质包括:
- 可以从底部弹出
- 可以上下拖拽布局
- 可以单击淡黑色遮罩隐藏/关闭
BottomSheetDialog
继承Dialog
,是一种对话框,它是拥有BottomSheetBehavior
行为的对话框,从而实现从底部弹出和上下拉伸的效果。
BottomSheetDialogFragment
是包含BottomSheetDialog
的片段(Fragment
),所以它可以同时利用Fragment
的特点和BottomSheet
这一交互效果。
BottomSheetBehavior
使用
在xml布局文件中与CoordinatorLayout
配合使用。如下设置后,NestedScrollView
具有BottomSheetBehavior
的性质,且一开始就显示在布局中,初始的显示高度为peekHeight
值。背景没有淡黑色遮罩,可以上下拖拽。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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不可见。
获取行为
在布局文件中声明之后,就可以在代码中获取行为了。
1 2 3 4
| 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
底页的监听回调事件。
1 2 3 4 5 6 7 8 9 10 11 12
| 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
。
1 2 3 4 5 6 7 8 9
| <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
</android.support.v4.widget.NestedScrollView>
|
使用——代码文件
使用上述的布局文件,假设名称为layout_bsd
。可以获取行为实现自定义。要注意的是从视图的父视图((View)view.getParent()
)获取底页行为,否则会报错:
The view is not a child of CoordinatorLayout.
1 2 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
。
用法一——披着Fragment
外衣的BottomSheetDialog
重写BottomSheetDialogFragment
中的onCreateDialog
方法。可以看到和上面BottomSheetDialog
的代码是相同的。同样可以在中间获取行为用于自定义。要注意是子视图的扩充用到的是View.inflate
的静态方法,否则会报错。
1 2 3 4 5 6 7 8 9
| @Override public 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; }
|
用法二——真正的Fragment
重写BottomSheetDialogFragment
中的onCreateView
方法。代码与Fragment
的常规写法相同。不过不好的一点是不容易获取行为。如果要强行获取行为的话,可以使用以下的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Override public 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);
|
1 2 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
| <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>
|
效果图
参考