阅读 1033

Android底部对话框神器BottomSheetDialog

前言

在以前想让底部弹出一个对话框,一般的做法是继承Dialog,然后设置布局、设置位置、设置高宽,如果还想增加一个从下到上的动画,还需要新建一个动画文件,但是到后来,我们有了官方提供的BottomSheetDialogFragmentBottomSheetDialogbottomSheetBehavior这几个类,几行代码就可以实现上述效果。

如下所示,就可以简单创建一个底部弹出的Dialog。

var bottomSheetDialog = BottomSheetDialog(this) bottomSheetDialog.setContentView(R.layout.dialog) bottomSheetDialog.show() 复制代码

录屏_选择区域_20210925170605.gif

还可以使用BottomSheetDialogFragmentBottomSheetDialogFragment继承自DialogFragment ,在需要的时候我们可以重写onCreateDialog方法,返回自定义的Dialog,他默认也是返回BottomSheetDialog

public class BottomSheetDialogFragment extends AppCompatDialogFragment {   private boolean waitingForDismissAllowingStateLoss;   @NonNull   @Override   public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {     return new BottomSheetDialog(getContext(), getTheme());   }   ..... } 复制代码

拖拽

如果视图中有很多东西要展示,默认可以只展示一部分,另一部分可以向上拖拽显示,这个具体是怎么计算的,可以继续往下看,在这里,只需要新建一个dialog布局,高度设置成match_parent即可。

class MemberBottomSheetDialog : BottomSheetDialogFragment() {     override fun onCreateView(         inflater: LayoutInflater,         container: ViewGroup?,         savedInstanceState: Bundle?     ): View? = inflater.inflate(R.layout.dialog, container, false) } MemberBottomSheetDialog().show(supportFragmentManager,"TAG") 复制代码

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:layout_width="match_parent"     android:background="#ffffff"     android:layout_height="match_parent">     <FrameLayout         android:layout_width="match_parent"         android:layout_height="match_parent">     </FrameLayout> </RelativeLayout> 复制代码

录屏_选择区域_20210925171341.gif

但是你会发现,只有当根布局是RelativeLayout的时候,内容才会显示出来。

修改高度

由于BottomSheetDialogFragment使用的是BottomSheetDialog,他默认的高度计算方式是parentHeight - parentWidth * 9 / 16,但是他对外提供了一个方法给我们,用来设置开始时候的状态,如下所示:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {     var bottomSheetDialog = BottomSheetDialog(context!!);     bottomSheetDialog.behavior.peekHeight=100;     return bottomSheetDialog } 复制代码

默认展开

还有个问题是,默认是不展开的,如果想展开,也就是全屏,可以设置stateBottomSheetBehavior.STATE_EXPANDED

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {     super.onViewCreated(view, savedInstanceState)          if (dialog is BottomSheetDialog) {         val behaviour = (dialog as BottomSheetDialog).behavior behaviour.state = BottomSheetBehavior.STATE_EXPANDED } } 复制代码

禁止拖拽

官方对setDraggable的解释是:设置是否可以通过拖动折叠/展开,禁用拖动时,应用程序需要实现自定义方式来展开/折叠对话框。

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {     val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog     bottomSheetDialog.behavior.isDraggable=false     return bottomSheetDialog } 复制代码

背景不变暗

默认情况下,弹出对话框时,会便暗,这其实加个样式就可以了。

<style name="myDialog" parent="Theme.MaterialComponents.BottomSheetDialog">     <item name="android:backgroundDimEnabled">false</item> </style> 复制代码

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {     return BottomSheetDialog(context!!, R.style.myDialog) } 复制代码

监听滚动

有时候还需要在向上拖拽时候做一些联动,就需要获取对话框滑动的值,可以通过behavior.addBottomSheetCallback来实现。

slideOffset的值是0-1之间,默认状态下是0,滑动到顶部的时候值是1,消失的时候值是-1,

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {     val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog     bottomSheetDialog.behavior.addBottomSheetCallback(object :         BottomSheetBehavior.BottomSheetCallback() {         override fun onStateChanged(bottomSheet: View, newState: Int) {             Log.i(TAG, "onStateChanged: ")         }         override fun onSlide(bottomSheet: View, slideOffset: Float) {             Log.i(TAG, "onSlide: ${slideOffset}")         }     })     return bottomSheetDialog } 复制代码

向上拖拽时候是怎么计算的?

这个问题很简单,他内部拖拽是通过ViewDragHelper方式来完成的,所以当我们不能向上拖拽的时候,通过ViewDragHelper有两种办法,一是在tryCaptureView中返回false,二是在clampViewPositionVertical中返回某个值,这个值返回一个最大的拖拽值,当我们拖拽到最顶部时候,就无法继续向上了。

上面我们说过,默认的高度是parentHeight - parentWidth * 9 / 16,但如果我们的View小于这个值,那么最终的高度是取最小的,也就是取这个View的值,这样的话,对话框中的内容已经就全部显示出来了,还要上移干什么?

但是如果我们的View高度大于这个值,他就会取parentHeight - parentWidth * 9 / 16这个值作为Dialog的高度,那么还可以上移多少空间?答案就是View的高度-parentHeight - parentWidth * 9 / 16

比如在我的手机上,parentHeight是2159,那么如果View取1600,那么就还可以上移40px,具体实现方式是定义了fitToContentsOffset变量,这个值就是parentHeight - childHeight,也就是屏幕中空闲的空间,当向上拖拽的时候会触发clampViewPositionVertical方法,他返回的值作为新的垂直位置,所以当拖拽到顶部时候,会返回fitToContentsOffset

BottomSheetBehavior实现底部Dialog

BottomSheetDialog中也是使用了BottomSheetBehavior来实现的,首先定义一个布局,注意根布局只能是CoordinatorLayout。

<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"     android:layout_height="match_parent"     tools:context=".MainActivity">     <LinearLayout         android:layout_width="match_parent"         android:layout_height="match_parent"         android:orientation="vertical">         <Button             android:id="@+id/bt"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="打开" />     </LinearLayout>     <LinearLayout         android:background="@color/cardview_dark_background"         android:id="@+id/design_bottom_sheet1"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:orientation="vertical"         app:behavior_hideable="true"         app:behavior_peekHeight="300dp"         app:elevation="6dp"         app:layout_behavior="@string/bottom_sheet_behavior"></LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> 复制代码

  var bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.design_bottom_sheet1));  bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);  findViewById<View>(R.id.bt).setOnClickListener {      if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN) {          bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED)      } else if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) {          bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN      }  }


作者:i听风逝夜
链接:https://juejin.cn/post/7011809908257456135

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐