Android Design Library之四:BottomSheetDialog

Magicer

简介

在我们的开发中经常会遇到从底部弹出对话框的需求。在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_dialogFrameLayout。代码如下:

    <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展示的时候,就调用了。BottomSheetBehaviorsetState(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_COLLAPSEDSTATE_EXPANDED的区别。 再说区别之前我们需要先看一个参数,mPeekHeight这个参数就是用来设置STATE_COLLAPSED状态下,界面显示的高度的。当不设置的时候会显示 view的全部。STATE_EXPANDED状态下则会显示出View的全部。还有一点需要注意的是设置STATE_HIDDEN的时候需要将mHideable设置为true。设置mPeekHeightmHideable的方法都有两种,分别为:

  • 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的全部。

参考:
BottomSheets的使用

阅读 4.8k

Magicer
android开发|Hexo

[链接]

246 声望
11 粉丝
0 条评论
你知道吗?

[链接]

246 声望
11 粉丝
宣传栏