BottomSheetDialog是原生实现的BottomSheet的dialog模式,适合很多场景下的底部弹窗功能,在此次记录下踩坑内容。
1. 关于布局的实现
BottomSheetDialog内部的布局实现及其简单,就是一个对CoordinatorLayout
的运用,XML原文如下:
<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>
其中id为design_bottom_sheet就是我们在调用setContentView()方法时往里塞的view的父布局,因此这个布局也就和实现圆角有关系了。
2.关于背景透明
网上其实有好多方法介绍如何让背景透明起来的
方法基本都是以下两种:
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));
mDialog.getWindow().findViewById(R.id.design_bottom_sheet)
.setBackgroundResource(android.R.color.transparent);
实际上可能在某些版本上可用,但是在笔者实际测试以后,发现这两个操作都没有实际上生效
明显可以看出是有个灰色的阴影效果无法去除,
最终,解决方案是:
if (mDialog.getWindow() != null) {
WindowManager.LayoutParams params = mDialog.getWindow().getAttributes();
params.dimAmount = 0.0f;
mDialog.getWindow().setAttributes(params);
}
这样设置下自己的dialog之后,就可以去掉那层灰色的样式了
这种情况下,就实现了没有阴影效果的底部弹出框了。
3. 关于底部弹窗的圆角
很多时候我们不止要实现底部弹窗,还要实现底部弹窗的圆角,那么该怎么处理呢?
其实只要在setContentView()放入的view的父布局加上需要的圆角shape就OK了
这个shape的圆角还有不懂怎么加的同学们请自觉百度吧
有的时候我们会改变一下底部弹窗的颜色,那么就会出现这个问题了
弹窗的圆角下班还有一点点白色,有经验的同学一看就明白了,那是因为咱们的view底部还有一个白色的父布局,这个父布局其实就是之前说的id是design_bottom_sheet的FrameLayout了,那么简单的处理一下就OK了
FrameLayout bottom = mDialog.findViewById(R.id.design_bottom_sheet);
if (bottom != null) {
bottom.setBackgroundResource(android.R.color.transparent);
}
这样把白色的父布局就会被处理掉了,咱们的圆角就比较合理的展示出来了。
然后,由于北京是透明的了,那么其实合理的运用一下padding和margin,就可以实现悬浮层的效果,这里就不赘述了。
4. 关于折叠
这个dialog又一个很有趣的现象,就是可以折叠,因为内部毕竟是一个
CoordinatorLayout实现的嘛,其实折叠主要是靠BottomSheetBehavior来进行的操作
BottomSheetBehavior<View> behavior = BottomSheetBehavior.
from((View) mDialogContentView.getParent());
可以通过这个方法来控制默认不折叠:
behavior.setSkipCollapsed(true);
这个方法用来控制可以全部收起,就是折叠之后在往下拉就会整个底部收起来:
behavior.setHideable(true);
这个方法来控制最小折叠高度:
behavior.setPeekHeight(xxx);
那么就有些同学发现,其实我默认展开之后,下拉会折叠到一个高度,然后需要再次下拉才能完全隐藏,那么怎么来取消这个方式呢,简单的来说就是通过上边的代码来实现的了,只要将这个方法的参数传入屏幕高度,那么就不会再有下拉会折叠一下的效果了
Display defaultDisplay = activity.getWindowManager().getDefaultDisplay();
Point point = new Point();
defaultDisplay.getSize(point);
behavior.setPeekHeight(point.y);
这样会隐藏底部弹出的那部分界面,但是没有真正的dismiss 当前的dialog
所以还需要加入以下监听,来隐藏dialog:
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
//当完全隐藏时,直接消除dialog
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
mDialog.dismiss();
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
5.带EditText的情况下,有关软键盘的问题:
很多时候我们隐藏软键盘会调用:
InputMethodManager imm = null;
imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (getWindow() != null) {
view = getWindow().getCurrentFocus();
}
if (null != view && imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
这种方法,但是在BottomSheetDialog的EditText使用时,会出现一个很有意思的问题,那就是view = getWindow().getCurrentFocus();这个返回的是null
那么实际隐藏软件盘的操作就是失败的了
如何避免这个问题呢,目前我使用的是自定义的BottomSheetDialog,通过这个dialog来处理隐藏
public class KeyBoardBottomSheetDialog extends BottomSheetDialog {
public KeyBoardBottomSheetDialog(@NonNull Context context) {
super(context);
}
public KeyBoardBottomSheetDialog(@NonNull Context context, int theme) {
super(context, theme);
}
protected KeyBoardBottomSheetDialog(@NonNull Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
@Override
public void dismiss() {
//因为dismiss之后当前焦点的EditText无法获取,所以自定义一下
hideKeyBoard();
super.dismiss();
}
public void hideKeyBoard() {
View view = null;
InputMethodManager imm = null;
imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (getWindow() != null) {
view = getWindow().getCurrentFocus();
}
if (null != view && imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
}
这样,只要调用自定义dialog的hideKeyBoard()方法,就可以实现
,就可以实现隐藏软键盘了。