简介
在我们的开发中经常会遇到从底部弹出对话框的需求。在design
包中,官方为我们提供了一种实现,就是BottomSheetDialog
。它的使用和dialog
一样,看下它的继承关系就知道了。
- 本文
design
包使用的版本为27.0.2
下面我们看下简单的使用代码。
BottomSheetDialog dialog = new BottomSheetDialog(this);
dialog.setContentView(R.layout.bottom_dialog_view);
dialog.show();
是的。你没有看错,这些代码就够了。
BottomSheetDialog
的兄弟还有个BottomSheetDialgFragment
,来看下BottomSheetDialogFragment
的代码。
public class BottomSheetDialogFragment extends AppCompatDialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new BottomSheetDialog(getContext(), getTheme());
}
}
是的。你还是没有看错,这就是全部的BottomSheetDialogFragment
的代码。内部就是个BottomSheetDialog
,就不做过多说明了。
代码实现
我们打开`BottomSheetDialog
的代码查看的话会发现,实现也很简单。只有二百行代码。来看一下。
我们在使用的时候会调用。setContentView()
方法,这里有三个重载的方法:
@Override
public void setContentView(@LayoutRes int layoutResId) {
super.setContentView(wrapInBottomSheet(layoutResId, null, null));
}
@Override
public void setContentView(View view) {
super.setContentView(wrapInBottomSheet(0, view, null));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
super.setContentView(wrapInBottomSheet(0, view, params));
}
我们可以看到最后都调用了wrapInBottomSheet()
方法。
private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
// #1
final FrameLayout container = (FrameLayout) View.inflate(getContext(),
R.layout.design_bottom_sheet_dialog, null);
final CoordinatorLayout coordinator =
(CoordinatorLayout) container.findViewById(R.id.coordinator);
//#2
if (layoutResId != 0 && view == null) {
view = getLayoutInflater().inflate(layoutResId, coordinator, false);
}
//#3
FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
mBehavior = BottomSheetBehavior.from(bottomSheet);
mBehavior.setBottomSheetCallback(mBottomSheetCallback);
mBehavior.setHideable(mCancelable);
if (params == null) {
bottomSheet.addView(view);
} else {
bottomSheet.addView(view, params);
}
//...省略部分代码
return container;
}
我们可以分两部分开,#2
部分就是获取到设置的View
这没什么好说的。 #1
获取了个design_bottom_sheet_dialog
的FrameLayout
。代码如下:
<FrameLayout
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/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<View
android:id="@+id/touch_outside"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false"
tools:ignore="UnusedAttribute"/>
<FrameLayout
android:id="@+id/design_bottom_sheet"
style="?attr/bottomSheetStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
app:layout_behavior="@string/bottom_sheet_behavior"/>
</android.support.design.widget.CoordinatorLayout>
</FrameLayout
这里就是一个FrameLayout
嵌套了个CoordinatorLayout
,再嵌套了一个View
用于点击收起Dialog
和底部的FrameLayout
用来显示我们为BottomSheetDialog
设置的视图。上面wrapInBottomSheet()
也就大致完了。
那么到底是怎么做到从底部弹出的呢?这边就那几行没说的了嘛。还用想,肯定是他们“搞的鬼”。
mBehavior = BottomSheetBehavior.from(bottomSheet);
通过这一行我们创建了一个BottomSheetBehavior
。而正式这个BottomSheetBehavior
让对话框从底部弹出的。
@Override
protected void onStart() {
super.onStart();
if (mBehavior != null) {
mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
当Dialog
展示的时候,就调用了。BottomSheetBehavior
的setState(BottomSheetBehavior.STATE_COLLAPSED)
,那为什么调用这个方法就能显示出来了呢?我们来看下BottomSheetBehavior
的简单介绍,之后我们就明白了。
BottomSheetBehavior
/**
* An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as
* a bottom sheet.
*/
public class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {...}
这是官方的解释。主要是用来和CoordinatorLayout
配合来实现底部展示效果的。
主要使用的函数是setState()
方法,状态有如下几个:
-
STATE_DRAGGING
: 被拖动的状态。 -
STATE_SETTLING
: 拖拽松开之后到达终点位置(collapsed or expanded)前的状态 -
STATE_EXPANDED
: 展开的状态 -
STATE_COLLAPSED
: 折叠的状态 -
STATE_HIDDEN
: 隐藏的状态。
在这里主要说一下STATE_COLLAPSED
和STATE_EXPANDED
的区别。 再说区别之前我们需要先看一个参数,mPeekHeight
这个参数就是用来设置STATE_COLLAPSED
状态下,界面显示的高度的。当不设置的时候会显示 view的全部。STATE_EXPANDED
状态下则会显示出View的全部。还有一点需要注意的是设置STATE_HIDDEN
的时候需要将mHideable
设置为true。设置mPeekHeight
和mHideable
的方法都有两种,分别为:
-
set方法
: setPeekHight(height) 和setHideable(hideable) -
xml
中定义参数:
app:behavior_hideable="true"
app:behavior_peekHeight="50dp"
通过设置不同的状态来展现出不同的效果。参考BottomSheetDialog
的实现我们就可以实现一个自己的底部弹出效果了。获取Behavior
我们可以使用:
mBehavior = BottomSheetBehavior.from(view);
在BottomSheetDialog
中我们在onStart()
方法中调用了。 mBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED)
,这个时候,我们为dialog设置的界面就显示出来了。那么为什么没有折叠的效果呢?因为BottomSheetDialog
中并没有设置mPeekHeight
的值。前面说了。当没有设置peekHeight
时,STATE_COLLAPSED
状态下会显示View
的全部。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。