BottomSheetDialog控件使用

一、前言:

Android Support Library 23.2里的 Design Support Library新加了一個(gè)Bottom Sheets控件,Bottom Sheets顧名思義就是底部操作控件,用于在屏幕底部創(chuàng)建一個(gè)可滑動(dòng)關(guān)閉的視圖,可以替代對(duì)話框和菜單瓶逃。
其中包含BottomSheets、BottomSheetDialog和BottomSheetDialogFragment三種可以使用扎拣。其中應(yīng)用較多的控件是BottomSheetDialog腌歉,主要運(yùn)用在界面底部分享列表,評(píng)論列表等避除,最近在知乎評(píng)論列表界面看到知乎和今日頭條運(yùn)用到了這個(gè)效怎披。

本文實(shí)現(xiàn)效果如下:

效果圖.png

二、使用:

BottomSheetDialog可以替代大多數(shù)網(wǎng)格顯示和列表展示的dialog和popupwindow瓶摆,默認(rèn)寬度撐滿凉逛,并且在BottomSheetDialog 區(qū)域中向下滑動(dòng)也讓對(duì)話框消失。
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);
        //初始化數(shù)據(jù)
        initData();
    }

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

        /**
         * 點(diǎn)擊事件
         */
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //第一個(gè)彈窗
                bottomSheet();
            }
        });
    }

    private void bottomSheet() {
        if (bottomSheetDialog == null) {
            //創(chuàng)建布局
            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);
            //設(shè)置點(diǎn)擊dialog外部不消失
            bottomSheetDialog.setCanceledOnTouchOutside(true);
            //核心代碼 解決了無(wú)法去除遮罩問(wèn)題
            bottomSheetDialog.getWindow().setDimAmount(0f);
            //設(shè)置布局
            bottomSheetDialog.setContentView(view);
            //用戶行為
            mDialogBehavior = BottomSheetBehavior.from((View) view.getParent());
            //dialog的高度
            mDialogBehavior.setPeekHeight(getWindowHeight());
        }
        //展示
        bottomSheetDialog.show();
        //重新用戶的滑動(dòng)狀態(tài)
        mDialogBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View view, int newState) {
                //監(jiān)聽(tīng)BottomSheet狀態(tài)的改變
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    bottomSheetDialog.dismiss();
                    mDialogBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }

            @Override
            public void onSlide(@NonNull View view, float v) {
                //監(jiān)聽(tīng)拖拽中的回調(diào),根據(jù)slideOffset可以做一些動(dòng)畫(huà)
            }
        });

    }


    /**
     * 計(jì)算高度(初始化可以設(shè)置默認(rèn)高度)
     *
     * @return
     */
    private int getWindowHeight() {
        Resources res = this.getResources();
        DisplayMetrics displayMetrics = res.getDisplayMetrics();
        int heightPixels = displayMetrics.heightPixels;
        //設(shè)置彈窗高度為屏幕高度的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="彈窗標(biāo)題"
            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,設(shè)置圓角有問(wèn)題 -->
    <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之外沒(méi)有焦點(diǎn)的區(qū)域是否罩上黑色半透明 主要是這個(gè)地方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,圓角沒(méi)問(wèn)題-->
    <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、使用過(guò)程中出現(xiàn)的問(wèn)題

當(dāng)我們向下滑動(dòng)BottomSheetDialog隱藏Dialog后穿撮,無(wú)法用bottomSheetDialog.show()再次打開(kāi)缺脉,為什么呢?我們先看下源碼的實(shí)現(xiàn):

@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(); //關(guān)鍵代碼
        }
    }

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

通過(guò)源碼文件我們可以看出悦穿,系統(tǒng)的BottomSheetDialog是基于BottomSheetBehavior封裝的攻礼,當(dāng)我們滑動(dòng)隱藏了BottomSheetBehavior中的View后,內(nèi)部是設(shè)置了BottomSheetBehavior的狀態(tài)為STATE_HIDDEN栗柒,接著它替我們關(guān)閉了Dialog秘蛔,所以我們?cè)俅握{(diào)用show()的時(shí)候Dialog沒(méi)法再此打開(kāi)狀態(tài)為HIDE的Dialog了。
查看了源文件傍衡,我們就通過(guò)復(fù)寫B(tài)ottomSheetCallback的回調(diào)方法深员,來(lái)實(shí)現(xiàn)我們的效果,這里就引入了BottomSheetBehavior蛙埂,下面先介紹BottomSheetBehavior的使用倦畅。

2、BottomSheetBehavior的作用

根據(jù)官方Api绣的,BottomSheetBehavior有一個(gè)靜態(tài)方法BottomSheetBehavior.from(View)叠赐,會(huì)返回這個(gè)View引用的BottomSheetBehavior,這個(gè)方法會(huì)檢查這個(gè)View是否是CoordinatorLayout的子View屡江,如果是就會(huì)得到這個(gè)View的Behavior芭概。通過(guò)BottomSheetBehavior,我們可以通過(guò)setPeekHeight(int height)設(shè)置dialog的顯示高度惩嘉,通過(guò)setBottomSheetCallback(callback)實(shí)現(xiàn)BottomSheetDialog的狀態(tài)監(jiān)聽(tīng)罢洲。其中,在BottomSheetCallback回調(diào)方法中,onStateChanged監(jiān)聽(tīng)狀態(tài)的改變,onSlide是拖拽的回調(diào),onStateChanged可以監(jiān)聽(tīng)到的回調(diào)一共有五種:

  /**
     * 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: 隱藏狀態(tài)文黎。默認(rèn)是false惹苗,可通過(guò)app:behavior_hideable屬性設(shè)置。
  • STATE_COLLAPSED: 折疊關(guān)閉狀態(tài)耸峭∽兀可通過(guò)app:behavior_peekHeight來(lái)設(shè)置顯示的高度,peekHeight默認(rèn)是0。
  • STATE_DRAGGING: 被拖拽狀態(tài)
  • STATE_SETTLING: 拖拽松開(kāi)之后到達(dá)終點(diǎn)位置(collapsed or expanded)前的狀態(tài)劳闹。
  • STATE_EXPANDED: 完全展開(kāi)的狀態(tài)院究。

那么如何獲取到BottomSheetDialog的BottomSheetBehavior呢洽瞬?

第一種:在BottomSheetDialog調(diào)用setContentView方法之后,調(diào)用

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

第二種:在BottomSheetDialog調(diào)用setContentView方法之后业汰,調(diào)用

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);
                //調(diào)用behavior相關(guān)方法
                ...             
                frameLayout.forceLayout();
}

三伙窃、總結(jié):

  • 1蔬胯、 BottomSheetDialog彈窗对供,可以設(shè)置初始化高度:
 //dialog的高度
mDialogBehavior.setPeekHeight(getWindowHeight());
  • 2、BottomSheetDialog根據(jù)內(nèi)容的高低氛濒,決定最大高度(最大高度是屏幕高度)确徙;

  • 3伴逸、背景的透明度設(shè)置

 //核心代碼 解決了無(wú)法去除遮罩問(wèn)題
bottomSheetDialog.getWindow().setDimAmount(0f);
  • 4、可窗外可點(diǎn)擊消失
 //設(shè)置點(diǎn)擊dialog外部不消失
bottomSheetDialog.setCanceledOnTouchOutside(true);
  • 5、彈窗樣式:
<!--BottomSheetDialog彈窗2,圓角沒(méi)問(wèn)題-->
    <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方法
 //重新用戶的滑動(dòng)狀態(tài)
        mDialogBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View view, int newState) {
                //監(jiān)聽(tīng)BottomSheet狀態(tài)的改變
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    bottomSheetDialog.dismiss();
                    mDialogBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }
            }

            @Override
            public void onSlide(@NonNull View view, float v) {
                //監(jiān)聽(tīng)拖拽中的回調(diào),根據(jù)slideOffset可以做一些動(dòng)畫(huà)
            }
        });
  • 7壮不、設(shè)置彈窗高度:
bottomSheetBehavior.setPeekHeight(120);

注意:這個(gè)是默認(rèn)的最小高度

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末菱阵,一起剝皮案震驚了整個(gè)濱河市晴及,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌世舰,老刑警劉巖动雹,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異跟压,居然都是意外死亡胰蝠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門震蒋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)茸塞,“玉大人,你說(shuō)我怎么就攤上這事查剖〖嘏埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵笋庄,是天一觀的道長(zhǎng)效扫。 經(jīng)常有香客問(wèn)我倔监,道長(zhǎng),這世上最難降的妖魔是什么菌仁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任浩习,我火速辦了婚禮,結(jié)果婚禮上济丘,老公的妹妹穿的比我還像新娘谱秽。我一直安慰自己,他們只是感情好摹迷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布疟赊。 她就那樣靜靜地躺著,像睡著了一般泪掀。 火紅的嫁衣襯著肌膚如雪听绳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天异赫,我揣著相機(jī)與錄音椅挣,去河邊找鬼。 笑死塔拳,一個(gè)胖子當(dāng)著我的面吹牛鼠证,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播靠抑,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼量九,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了颂碧?” 一聲冷哼從身側(cè)響起荠列,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎载城,沒(méi)想到半個(gè)月后肌似,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诉瓦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年川队,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睬澡。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡固额,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煞聪,到底是詐尸還是另有隱情斗躏,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布昔脯,位于F島的核電站瑟捣,受9級(jí)特大地震影響馋艺,放射性物質(zhì)發(fā)生泄漏栅干。R本人自食惡果不足惜迈套,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碱鳞。 院中可真熱鬧桑李,春花似錦、人聲如沸窿给。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崩泡。三九已至禁荒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間角撞,已是汗流浹背呛伴。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谒所,地道東北人热康。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像劣领,于是被迫代替她去往敵國(guó)和親姐军。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容