首页 > 技术文章 > BottomSheet和BottomSheetDialog随记

ekikun 2021-05-10 14:59 原文

BottomSheets

BottomSheet

BottomSheet,我的理解是一种view的呈现样式。如果把相应的view添加下列的属性

采用:

<app:layout_behavior="@string/bottom_sheet_behavior>

即可声明为BottomSheet类型的view,但是其父Layout必须为CoordinatorLayout

BottomSheet的五种状态:

BottmSheet在展开,隐藏,折叠的情况下,会设置为不同的状态:

  • STATE_COLLAPSED

    默认的折叠状态, bottom sheets只在底部显示一部分布局。显示高度可以通过app:behavior_peekHeight 或者在代码中获取BottomSheetBehavior实例设置,默认为0

  • STATE_DRAGGING

    ​ 过渡状态,此时用户正在向上或者向下拖动bottom sheet

  • STATE_SETTLING

    ​ 视图从脱离手指自由滑动到最终停下的这一小段时间

  • STATE_EXPANDED

    bottom sheet 处于完全展开的状态:当bottom sheet的高度低于CoordinatorLayout容器时,整个bottom sheet都可见;或者CoordinatorLayout容器已经被bottom sheet填满。

  • STATE_HIDDEN

    默认无此状态(可通过app:behavior_hideable 启用此状态),启用后用户将能通过向下滑动完全隐藏 bottom sheet。

BottomSheetDialog

BottomSheetDialogd是用BottomSheet的方式封装的Dialog, 它可以让上层View不用必须指定为CoordinatorLayout 。它可以实现类似力扣评论区的效果, 从底部弹出一个Dialog,然后会阻断主窗口,让其呈灰黑色(调用了onPause()回调), 呈灰黑色也是可以去掉的。

设置BottomSheet的样式

layout文件中编写页面,样式例如颜色,圆角在value资源文件中用style来编写,圆角的shape在drawable中定义一个shape文件来编写。

获取view和绑定控件

在代码中,通过inflate BottomSheetDialog的layout来获取到Dialog的样式,然后按照常规的方法来添加逻辑或者动态修改UI

配合BottomSheetBehavior设置高度

定义一个BottomSheetBehavior, 为了让其和BottomSheetDialog相关联,需要在dialog设置了contentview之后构造,以让它获取一个父布局.

dialog.setContentView(view);
BottomSheetBehavior behavior = BottomSheetBehavior.from((View)view.getParent());

在BottomSheetBehavior的回调中,在改变状态时,通过newState参数判断改变到什么状态,并添加相应的逻辑

behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
      if(newState==BottomSheetBehavior.STATE_EXPANDED){
       ViewGroup.LayoutParams params = bottomSheet.getLayoutParams();
                    params.height = behavior.getPeekHeight();
                    bottomSheet.setLayoutParams(params);
      
}

    
//下列函数在滑动的时候回调,会修改slideOffset;
// 从peekHight往下,slideOffset取[-1,0]; 往上到maxHight, 取[0,1]    
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {

	}
);

以上就是设置view的最大高度的方法,demo把最大高度设置为了和peekhight相同的高度,这样可以把BottomSheetDialog设置为固定高度。上面的ViewGroup可以简单理解为这个view所处的layout,我理解为是它的父布局,所以调整父布局的高度就调整了整体的高度,在代码中,修改的LayoutParams来实现这个目的。

但在开发中,采用上述的方法会有一个问题:由于默认的弹起状态是STATE_COLLAPSED, 即使设置了固定高度,在第一次弹出时,仍然可以拉起到一个很高的高度,然后再切换到STATE_EXPANDED状态,高度为我们预设的高度,这和我们看到主流软件做出来的效果有差异。

解决方法如下:

// 先将状态预置为STATE_EXPANDED,然后再弹出Dialog。弹起之后就是固定的,也没有滑动冲突。
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = behavior.getPeekHeight();
view.setLayoutParams(params);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
dialog.show();

去除灰黑色背景效果

抖音评论区也是用的BottomSheetDialog,但是抖音评论区在弹起后背景不会变为灰黑色。

设置代码如下,设置在dialog.show()之前

if(dialog.getWindow()!=null){
	WindowManager.LayoutParams wParams = dialog.getWindow().getAttributes();
	wParams.dimAmount = 0.0f; // 完全不暗,背景可见
	dialog.getWindow().setAttributes(wParams);
}

WindowManager用于管理Window,它是显示View的最底层,通过view可以获取到它。WindowManager.LayoutParams 继承于ViewGroup.LayoutParams,用于设置Window的参数

推荐阅读