Material Design系列之BottomSheet详解

Material Design系列之BottomSheet详解

BottomSheetBehavior官方文档

BottomSheetDialog官方文档

BottomSheetDialogFragment官方文档

Material Design官方文档Sheets: bottom的介绍

简介

BottomSheet可以理解为底部对话框,类似popwindow实现的效果

BottomSheet有两种类型:

一、没有蒙层,可以对没有遮盖住的地方进行操作,类似百度地图查询路线的页面;


图片1

二、有蒙层效果,就是和正常的popwindow使用效果一样了。


图片2
使用

compile 'com.android.support:design:26.0.0-alpha1'

1、BottomSheetBehavior的使用

依赖于CoordinatorLayout和BottomSheetBehavior,需要将底部菜单布局作为CoordinatorLayout的子View,实现简单但不够灵活,适用于底部菜单布局稳定的情况。

<android.support.design.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"
    android:orientation="vertical"
    tools:context="com.wangshuai.androidui.material.BottomSheetActivity">

    <include layout="@layout/content_main" />

    <include layout="@layout/content_bottom_sheet" />


</android.support.design.widget.CoordinatorLayout>

content_bottom_sheet布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:id="@+id/ll_content_bottom_sheet"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              app:behavior_hideable="true"
              app:behavior_peekHeight="50dp"
              app:layout_behavior="@string/bottom_sheet_behavior"
    android:background="@color/white">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="人生若只如初见,何事秋风悲画扇。"
        android:textSize="20sp"
        android:gravity="center"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="等闲变却故人心,却道故人心易变。"
        android:textSize="20sp"
        android:gravity="center"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="骊山语罢清宵半,泪雨霖铃终不怨。"
        android:textSize="20sp"
        android:gravity="center"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="何如薄幸锦衣郎,比翼连枝当日愿。"
        android:textSize="20sp"
        android:gravity="center"/>

</LinearLayout>

其中app:behavior_hideable="true"表示可以让bottom sheet完全隐藏,默认为false;

app:behavior_peekHeight="60dp"表示当为STATE_COLLAPSED(折叠)状态的时候bottom sheet残留的高度,默认为0。

app:layout_behavior="@string/bottom_sheet_behavior"这个一定要设置,不然起不到效果。

通过BottomSheetBehavior控制content_bottom_sheet布局的显示和隐藏

bottomSheetBehavior = BottomSheetBehavior.from(llContentBottomSheet);
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {

                switch (newState) {
                    case BottomSheetBehavior.STATE_COLLAPSED:
                        Log.e("Bottom Sheet Behaviour", "STATE_COLLAPSED");
                        break;
                    case BottomSheetBehavior.STATE_DRAGGING:
                        Log.e("Bottom Sheet Behaviour", "STATE_DRAGGING");
                        break;
                    case BottomSheetBehavior.STATE_EXPANDED:
                        Log.e("Bottom Sheet Behaviour", "STATE_EXPANDED");
                        break;
                    case BottomSheetBehavior.STATE_HIDDEN:
                        Log.e("Bottom Sheet Behaviour", "STATE_HIDDEN");
                        break;
                    case BottomSheetBehavior.STATE_SETTLING:
                        Log.e("Bottom Sheet Behaviour", "STATE_SETTLING");
                        break;
                }
            }

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

            }
        });

            case R.id.btn_expand://展开
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                break;
            case R.id.btn_collapsed://折叠
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                break;
            case R.id.btn_hide://隐藏
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
                break;

Bottom Sheet有五种状态:

  • STATE_COLLAPSED:折叠状态,bottom sheets只在底部显示一部分布局。显示高度可以通过 app:behavior_peekHeight 设置;
  • STATE_DRAGGING:过渡状态,此时用户正在向上或者向下拖动bottom sheet;
  • STATE_SETTLING: 视图从脱离手指自由滑动到最终停下的这一小段时间
  • STATE_EXPANDED: 完全展开的状态
  • STATE_HIDDEN: 隐藏状态。默认是false,可通过app:behavior_hideable属性设置是否能隐藏

这种使用方法是没有蒙层,可以对其他控件进行操作。

2、BottomSheetDialog的使用

BottomSheetDialog的使用就和对话框差不多。会出现蒙层,只能对弹出的页面进行操作。

                if (dialog == null){
                    dialog = new BottomSheetDialog(this);
                }
                dialog.setCancelable(false);
                dialog.setCanceledOnTouchOutside(true);
                View view =LayoutInflater.from(BottomSheetActivity.this).inflate(R.layout.content_bottom_sheet_dialog,null);
                TextView tvWechat = view.findViewById(R.id.tv_wechat);
                tvWechat.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(BottomSheetActivity.this,"微信",Toast.LENGTH_SHORT).show();
                        dialog.dismiss();
                    }
                });
                dialog.setContentView(view);
                dialog.show();

BottomSheetDialog部分源码:

private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
        final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
                R.layout.design_bottom_sheet_dialog, null);
        if (layoutResId != 0 && view == null) {
            view = getLayoutInflater().inflate(layoutResId, coordinator, false);
        }
        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);
        }
        // We treat the CoordinatorLayout as outside the dialog though it is technically inside
        coordinator.findViewById(R.id.touch_outside).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mCancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
                    cancel();
                }
            }
        });
        // Handle accessibility events
        ViewCompat.setAccessibilityDelegate(bottomSheet, new AccessibilityDelegateCompat() {
            @Override
            public void onInitializeAccessibilityNodeInfo(View host,
                    AccessibilityNodeInfoCompat info) {
                super.onInitializeAccessibilityNodeInfo(host, info);
                if (mCancelable) {
                    info.addAction(AccessibilityNodeInfoCompat.ACTION_DISMISS);
                    info.setDismissable(true);
                } else {
                    info.setDismissable(false);
                }
            }

            @Override
            public boolean performAccessibilityAction(View host, int action, Bundle args) {
                if (action == AccessibilityNodeInfoCompat.ACTION_DISMISS && mCancelable) {
                    cancel();
                    return true;
                }
                return super.performAccessibilityAction(host, action, args);
            }
        });
        bottomSheet.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                // Consume the event and prevent it from falling through
                return true;
            }
        });
        return coordinator;
    }

由源码可知,BottomSheetDialog的setContentView最终是被CoordinatorLayout包裹住。

3、BottomSheetDialogFragment的使用

BottomSheetDialogFragment的使用和fragment一样。可以帮助我们实现全屏的BottomSheet展示效果

CustomFragmentDialog fragmentDialog = new CustomFragmentDialog();
fragmentDialog.show(getSupportFragmentManager(),"CustomFragmentDialog");

public class CustomFragmentDialog extends BottomSheetDialogFragment {
    private ArrayList<String> list = new ArrayList<>();

    public CustomFragmentDialog() {
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.content_bottom_sheet_fragment_dialog,container, false);
        initViews(view);
        return view;
    }

    private void initViews(View view) {
        for (int i = 0;i < 100;i++){
            list.add("条目"+i);
        }
        RecyclerView recyclerView = view.findViewById(R.id.rv_item);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(list);
        recyclerView.setAdapter(adapter);
    }

}

/**
 * 全屏显示
 */
public class FullSheetDialogFragment extends BottomSheetDialogFragment{

    private BottomSheetBehavior mBehavior;
    private ArrayList<String> list = new ArrayList<>();

    public FullSheetDialogFragment() {
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
        View view = View.inflate(getContext(), R.layout.content_bottom_sheet_fragment_dialog, null);
        initViews(view);
        dialog.setContentView(view);
        mBehavior = BottomSheetBehavior.from((View) view.getParent());

        return dialog;
    }

    private void initViews(View view) {
        for (int i = 0;i < 100;i++){
            list.add("条目"+i);
        }
        RecyclerView recyclerView = view.findViewById(R.id.rv_item);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(list);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onStart()
    {
        super.onStart();
        mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);//全屏展开
    }

}
使用bottom sheet的问题

BottomSheetDialog沉浸式的一些坑

解决使用BottomSheetDialog时状态栏变黑的问题

Github示例代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容