美文网首页
BottomSheetDialog控件使用

BottomSheetDialog控件使用

作者: 因为我的心 | 来源:发表于2020-11-16 12:16 被阅读0次

一、前言:

Android Support Library 23.2里的 Design Support Library新加了一个Bottom Sheets控件,Bottom Sheets顾名思义就是底部操作控件,用于在屏幕底部创建一个可滑动关闭的视图,可以替代对话框和菜单。
其中包含BottomSheets、BottomSheetDialog和BottomSheetDialogFragment三种可以使用。其中应用较多的控件是BottomSheetDialog,主要运用在界面底部分享列表,评论列表等,最近在知乎评论列表界面看到知乎和今日头条运用到了这个效。

本文实现效果如下:

效果图.png

二、使用:

BottomSheetDialog可以替代大多数网格显示和列表展示的dialog和popupwindow,默认宽度撑满,并且在BottomSheetDialog 区域中向下滑动也让对话框消失。
gitHub地址:https://gitee.com/luoyanyong/BottomSheetDemo

1、依赖:

 implementation 'com.google.android.material:material:1.0.0'

2、MainActivity

public class MainActivity extends AppCompatActivity {
    private Button btn1;
    private BottomSheetDialog bottomSheetDialog;
    private BottomSheetBehavior mDialogBehavior;
    private List<Integer> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn1 = findViewById(R.id.btn1);
        //初始化数据
        initData();
    }

    private void initData() {
        list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(i);
        }

        /**
         * 点击事件
         */
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //第一个弹窗
                bottomSheet();
            }
        });
    }

    private void bottomSheet() {
        if (bottomSheetDialog == null) {
            //创建布局
            View view = LayoutInflater.from(this).inflate(R.layout.dialog_bottomsheet, null, false);
            RecyclerView recyclerView = view.findViewById(R.id.dialog_recycleView);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            MainAdapter mainAdapter = new MainAdapter(R.layout.item_main, list);
            recyclerView.setAdapter(mainAdapter);
            bottomSheetDialog = new BottomSheetDialog(this, R.style.BottomSheetDialog);
            //设置点击dialog外部不消失
            bottomSheetDialog.setCanceledOnTouchOutside(true);
            //核心代码 解决了无法去除遮罩问题
            bottomSheetDialog.getWindow().setDimAmount(0f);
            //设置布局
            bottomSheetDialog.setContentView(view);
            //用户行为
            mDialogBehavior = BottomSheetBehavior.from((View) view.getParent());
            //dialog的高度
            mDialogBehavior.setPeekHeight(getWindowHeight());
        }
        //展示
        bottomSheetDialog.show();
        //重新用户的滑动状态
        mDialogBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View view, int newState) {
                //监听BottomSheet状态的改变
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    bottomSheetDialog.dismiss();
                    mDialogBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }

            @Override
            public void onSlide(@NonNull View view, float v) {
                //监听拖拽中的回调,根据slideOffset可以做一些动画
            }
        });

    }


    /**
     * 计算高度(初始化可以设置默认高度)
     *
     * @return
     */
    private int getWindowHeight() {
        Resources res = this.getResources();
        DisplayMetrics displayMetrics = res.getDisplayMetrics();
        int heightPixels = displayMetrics.heightPixels;
        //设置弹窗高度为屏幕高度的3/4
        return heightPixels - heightPixels / 4;
    }

}

2、dialog_bottomsheet.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/round_top"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="40dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="弹窗标题"
            android:textStyle="bold" />

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="10dp"
            android:src="@drawable/ic_launcher_background" />
    </RelativeLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/dialog_recycleView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

3、圆角

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:shape="rectangle"
    tools:ignore="MissingDefaultResource">
    <solid android:color="#FFCACACA" />
    <corners
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"
        />

</shape>

4、BottomSheetDialog的弹窗样式

  <!--BottomSheetDialog弹窗1,设置圆角有问题 -->
    <style name="BottomSheetDialogStyle" parent="Theme.Design.BottomSheetDialog">
        <!--是否浮在窗口之上-->
        <item name="android:windowIsFloating">true</item>
        <!--半透明-->
        <item name="android:windowIsTranslucent">true</item>
        <!--是否显示title-->
        <item name="android:windowNoTitle">true</item>
        <!--dialog之外没有焦点的区域是否罩上黑色半透明 主要是这个地方false表示不要遮罩-->
        <item name="android:backgroundDimEnabled">false</item>

        <item name="android:windowFrame">@null</item>
        <item name="android:background">@android:color/transparent</item>
        <item name="android:windowBackground">@android:color/transparent</item>

    </style>


    <!--BottomSheetDialog弹窗2,圆角没问题-->
    <style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
    </style>
    <style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@android:color/transparent</item>
    </style>

5、item_main布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal"
    android:id="@+id/rl_item"
    >

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="条目1"
        android:layout_centerVertical="true"
        android:layout_marginLeft="15dp"
        android:textSize="15sp"
        />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/oval_red"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="15dp"
        />
</RelativeLayout>

三、解析:

1、使用过程中出现的问题

当我们向下滑动BottomSheetDialog隐藏Dialog后,无法用bottomSheetDialog.show()再次打开,为什么呢?我们先看下源码的实现:

@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
    super.setContentView(wrapInBottomSheet(0, view, params));
}

private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
    final CoordinatorLayout coordinator = View.inflate(getContext(),R.layout...., null);
    FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
    BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback);
    ...
    return coordinator;
}

private BottomSheetCallback mBottomSheetCallback = new BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_HIDDEN) {
            dismiss(); //关键代码
        }
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
    }
};

通过源码文件我们可以看出,系统的BottomSheetDialog是基于BottomSheetBehavior封装的,当我们滑动隐藏了BottomSheetBehavior中的View后,内部是设置了BottomSheetBehavior的状态为STATE_HIDDEN,接着它替我们关闭了Dialog,所以我们再次调用show()的时候Dialog没法再此打开状态为HIDE的Dialog了。
查看了源文件,我们就通过复写BottomSheetCallback的回调方法,来实现我们的效果,这里就引入了BottomSheetBehavior,下面先介绍BottomSheetBehavior的使用。

2、BottomSheetBehavior的作用

根据官方Api,BottomSheetBehavior有一个静态方法BottomSheetBehavior.from(View),会返回这个View引用的BottomSheetBehavior,这个方法会检查这个View是否是CoordinatorLayout的子View,如果是就会得到这个View的Behavior。通过BottomSheetBehavior,我们可以通过setPeekHeight(int height)设置dialog的显示高度,通过setBottomSheetCallback(callback)实现BottomSheetDialog的状态监听。其中,在BottomSheetCallback回调方法中,onStateChanged监听状态的改变,onSlide是拖拽的回调,onStateChanged可以监听到的回调一共有五种:

  /**
     * The bottom sheet is dragging.
     */
    public static final int STATE_DRAGGING = 1;

    /**
     * The bottom sheet is settling.
     */
    public static final int STATE_SETTLING = 2;

    /**
     * The bottom sheet is expanded.
     */
    public static final int STATE_EXPANDED = 3;

    /**
     * The bottom sheet is collapsed.
     */
    public static final int STATE_COLLAPSED = 4;

    /**
     * The bottom sheet is hidden.
     */
    public static final int STATE_HIDDEN = 5;
  • STATE_HIDDEN: 隐藏状态。默认是false,可通过app:behavior_hideable属性设置。
  • STATE_COLLAPSED: 折叠关闭状态。可通过app:behavior_peekHeight来设置显示的高度,peekHeight默认是0。
  • STATE_DRAGGING: 被拖拽状态
  • STATE_SETTLING: 拖拽松开之后到达终点位置(collapsed or expanded)前的状态。
  • STATE_EXPANDED: 完全展开的状态。

那么如何获取到BottomSheetDialog的BottomSheetBehavior呢?

第一种:在BottomSheetDialog调用setContentView方法之后,调用

BottomSheetBehavior mDialogBehavior = BottomSheetBehavior.from((View) mContentView.getParent());

第二种:在BottomSheetDialog调用setContentView方法之后,调用

final FrameLayout frameLayout = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
frameLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                frameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                BottomSheetBehavior behavior = BottomSheetBehavior.from(frameLayout);
                //调用behavior相关方法
                ...             
                frameLayout.forceLayout();
}

三、总结:

  • 1、 BottomSheetDialog弹窗,可以设置初始化高度:
 //dialog的高度
mDialogBehavior.setPeekHeight(getWindowHeight());
  • 2、BottomSheetDialog根据内容的高低,决定最大高度(最大高度是屏幕高度);

  • 3、背景的透明度设置

 //核心代码 解决了无法去除遮罩问题
bottomSheetDialog.getWindow().setDimAmount(0f);
  • 4、可窗外可点击消失
 //设置点击dialog外部不消失
bottomSheetDialog.setCanceledOnTouchOutside(true);
  • 5、弹窗样式:
<!--BottomSheetDialog弹窗2,圆角没问题-->
    <style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
    </style>
    <style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@android:color/transparent</item>
    </style>
  • 6、重新mDialogBehavior方法
 //重新用户的滑动状态
        mDialogBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View view, int newState) {
                //监听BottomSheet状态的改变
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    bottomSheetDialog.dismiss();
                    mDialogBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }

            @Override
            public void onSlide(@NonNull View view, float v) {
                //监听拖拽中的回调,根据slideOffset可以做一些动画
            }
        });
  • 7、设置弹窗高度:
bottomSheetBehavior.setPeekHeight(120);

注意:这个是默认的最小高度

参考:

相关文章

网友评论

      本文标题:BottomSheetDialog控件使用

      本文链接:https://www.haomeiwen.com/subject/pqvabktx.html