Bottom Sheet使用教程

ahdubi 8年前發布 | 8K 次閱讀 安卓開發 Android開發 移動開發

什么是Bottom Sheet?

Bottom Sheet是Design Support Library23.2 版本引入的一個類似于對話框的控件,可以暫且叫做底部彈出框吧。 Bottom Sheet中的內容默認是隱藏起來的,只顯示很小一部分,可以通過在代碼中設置其狀態或者手勢操作將其完全展開,或者完全隱藏,或者部分隱藏。對于Bottom Sheet的描述可以在官網查詢: https://material.io/guidelines/components/bottom-sheets.html#

其實在 Bottom Sheet出現之前已經有人實現了相同的功能,最早的一個可靠版本應該是 AndroidSlidingUpPanel ,當然它實現的原理跟谷歌的方式完全不一樣。

Bottom Sheet的類型

有兩種類型的Bottom Sheet:

1.Persistent bottom sheet:- 通常用于顯示主界面之外的額外信息,它是主界面的一部分,只不過默認被隱藏了,其深度(elevation)跟主界面處于同一級別;還有一個重要特點是在Persistent bottom sheet打開的時候,主界面仍然是可以操作的。ps:Persistent bottom sheet該如何翻譯呢?我覺得翻譯為普通bottom sheet就好了,還看到有人翻譯為“常駐bottom sheet”,可能更接近于英語的字面意思,可是反而不易理解。

2. 模態bottom sheet :- 顧名思義,模態的bottom sheet在打開的時候會阻止和主界面的交互,并且在視覺上會在bottom sheet背后加一層半透明的陰影,使得看上去深度(elevation)更深。

總結起來這兩種Bottom Sheet的區別主要在于視覺和交互上,當然適用方法也是不一樣的。

基本用法

不管是普通bottom sheet還是模態的bottom sheet,都需要依賴:

dependencies {
    ...
    compile 'com.android.support:design:24.1.1'
}

當然現在的app一般都要依賴這個兼容庫,版本號只要保證是在23.2.0及其以后就可以了。

Persistent bottom sheet的用法

其實Persistent bottom sheet不能算是一個控件,因為它只是一個普通的布局在CoordinatorLayout這個布局之下所表現出來的特殊行為。所以其使用方式跟普通的控件也很不一樣,它必須在CoordinatorLayout中,并且是CoordinatorLayout的直接子view。

定義主界面與bottom sheet的布局

為了讓xml代碼看起來不那么長,我們把布局分為content_main和content_bottom_sheet兩部分,content_main主要是一些按鈕,用于切換bottom sheet的狀態,content_bottom_sheet才是bottom sheet的內容。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/AppTheme.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

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

<!-- Main Content -->
<include layout="@layout/content_main" />

<!-- Bottom Sheet Content -->
<include layout="@layout/content_bottom_sheet" />


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

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="

<Button
    android:id="@+id/expand_bottom_sheet_button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/text_expand_bottom_sheet" />
<Button
    android:id="@+id/collapse_bottom_sheet_button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/expand_bottom_sheet_button"
    android:text="@string/text_collapse_bottom_sheet" />
<Button
    android:id="@+id/hide_bottom_sheet_button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/collapse_bottom_sheet_button"
    android:text="@string/text_hide_bottom_sheet" />
<Button
    android:id="@+id/show_bottom_sheet_dialog_button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/hide_bottom_sheet_button"
    android:text="@string/text_show_bottom_sheet_dialog" />

</RelativeLayout></code></pre>

content_bottom_sheet.xml

這里定義的布局就是bottom sheet的界面。這里是一個相對布局,其實你可以定義任意布局,唯一的要求是需要定義app:layout_behavior="@string/bottom_sheet_behavior",定義了這個屬性就相當于告訴了CoordinatorLayout這個布局是一個bottom sheet,它的顯示和交互都和普通的view不同。@string/bottom_sheet_behavior是一個定義在支持庫中的字符串,等效于android.support.design.widget.BottomSheetBehavior。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="

<TextView
    android:id="@+id/bottomSheetHeading"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/text_expand_me"
    android:textAppearance="@android:style/TextAppearance.Large" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/bottomSheetHeading"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="@dimen/activity_horizontal_margin"
    android:text="@string/text_welcome_message"
    android:textAppearance="@android:style/TextAppearance.Large" />

</RelativeLayout></code></pre>

其實你還可以看到這里除了app:layout_behavior之外,還有兩個屬性

    app:behavior_hideable="true"
    app:behavior_peekHeight="60dp"

其中app:behavior_hideable="true"表示你可以讓bottom sheet完全隱藏,默認為false;app:behavior_peekHeight="60dp"表示當為STATE_COLLAPSED(折疊)狀態的時候bottom sheet殘留的高度,默認為0。

當我們按照上面得代碼配置好布局之后,其實一個bottom sheet就已經完成了,在CoordinatorLayout和bottom_sheet_behavior的共同作用下,content_bottom_sheet布局就成了一個bottom sheet,  但是我們還需要知道如何控制它。

控制Persistent bottom sheet

我們在MainActivity.java中添加一些代碼,以處理bottom sheet,以及監聽bottom sheet狀態變化。

bottom sheet有以下5種狀態

  • STATE_COLLAPSED: 默認的折疊狀態, bottom sheets只在底部顯示一部分布局。顯示高度可以通過 app:behavior_peekHeight 設置(默認是0)

  • STATE_DRAGGING : 過渡狀態,此時用戶正在向上或者向下拖動bottom sheet

  • STATE_SETTLING: 視圖從脫離手指自由滑動到最終停下的這一小段時間

  • STATE_EXPANDED: bottom sheet 處于完全展開的狀態:當bottom sheet的高度低于CoordinatorLayout容器時,整個bottom sheet都可見;或者CoordinatorLayout容器已經被bottom sheet填滿。

  • STATE_HIDDEN : 默認無此狀態(可通過app:behavior_hideable 啟用此狀態),啟用后用戶將能通過向下滑動完全隱藏 bottom sheet

bottom sheet的狀態是通過BottomSheetBehavior來設置的,因此需要先得到BottomSheetBehavior對象,然后調用BottomSheetBehavior.setState()來設置狀態,比如設置為折疊狀態:

BottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

我們還可以通過BottomSheetBehavior.getState() 來獲得狀態。

要監聽bottom sheet的狀態變化則使用setBottomSheetCallback方法,之所以需要監聽是因為bottom sheet的狀態還可以通過手勢來改變。

具體使用見下面的代碼:

MainActivity.java

package com.androidtutorialshub.bottomsheets;

import android.os.Bundle; import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

// BottomSheetBehavior variable
private BottomSheetBehavior bottomSheetBehavior;

// TextView variable
private TextView bottomSheetHeading;

// Button variables
private Button expandBottomSheetButton;
private Button collapseBottomSheetButton;
private Button hideBottomSheetButton;
private Button showBottomSheetDialogButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initViews();
    initListeners();


}

/**
 * method to initialize the views
 */
private void initViews() {
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottomSheetLayout));
    bottomSheetHeading = (TextView) findViewById(R.id.bottomSheetHeading);
    expandBottomSheetButton = (Button) findViewById(R.id.expand_bottom_sheet_button);
    collapseBottomSheetButton = (Button) findViewById(R.id.collapse_bottom_sheet_button);
    hideBottomSheetButton = (Button) findViewById(R.id.hide_bottom_sheet_button);
    showBottomSheetDialogButton = (Button) findViewById(R.id.show_bottom_sheet_dialog_button);


}


/**
 * method to initialize the listeners
 */
private void initListeners() {
    // register the listener for button click
    expandBottomSheetButton.setOnClickListener(this);
    collapseBottomSheetButton.setOnClickListener(this);
    hideBottomSheetButton.setOnClickListener(this);
    showBottomSheetDialogButton.setOnClickListener(this);

    // Capturing the callbacks for bottom sheet
    bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(View bottomSheet, int newState) {

            if (newState == BottomSheetBehavior.STATE_EXPANDED) {
                bottomSheetHeading.setText(getString(R.string.text_collapse_me));
            } else {
                bottomSheetHeading.setText(getString(R.string.text_expand_me));
            }

            // Check Logs to see how bottom sheets behaves
            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(View bottomSheet, float slideOffset) {

        }
    });


}

/**
 * onClick Listener to capture button click
 *
 * @param v
 */
@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.collapse_bottom_sheet_button:
            // Collapsing the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            break;
        case R.id.expand_bottom_sheet_button:
            // Expanding the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            break;
        case R.id.hide_bottom_sheet_button:
            // Hiding the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
            break;
        case R.id.show_bottom_sheet_dialog_button:

            break;

    }
}

}</code></pre>

模態bottom sheet的用法

模態bottom sheet用法跟傳統的dialog很類似,它是一個BottomSheetDialogFragment。

首先定義好BottomSheetDialogFragment的布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="

<TextView
    android:id="@+id/bottomSheetHeading"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/text_dialog_bottom_sheet"
    android:textAppearance="@android:style/TextAppearance.Large" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@id/bottomSheetHeading"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="@dimen/activity_horizontal_margin"
    android:text="@string/text_welcome_message"
    android:textAppearance="@android:style/TextAppearance.Large" />

</RelativeLayout></code></pre>

注意這里不再需要定義behavior 和peekHeight之類的東西了。

創建一個繼承了BottomSheetDialogFragment的CustomBottomSheetDialogFragment 類,在onCreateView方法中把上面的布局傳遞進去

package com.androidtutorialshub.bottomsheets;

import android.os.Bundle; import android.support.design.widget.BottomSheetDialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;

public class CustomBottomSheetDialogFragment extends BottomSheetDialogFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.content_dialog_bottom_sheet, container, false); return v; }

}</code></pre>

顯示這個模態的bottom sheet

 new CustomBottomSheetDialogFragment().show(getSupportFragmentManager(), "Dialog");

與普通bottom sheet不同的是我們不需要處理它的狀態了,因為它跟普通bottom sheet機制都不同,只有打開和關閉狀態,而且是通過點擊bottom sheet之外的區域來取消bottom sheet的。

總結

由此可以看到 Persistent bottom sheet是 最復雜的而 模態bottom sheet 基本沒什么新東西。

在Persistent bottom sheet使用方法小節中我們是點擊一個item切換一個狀態,實際使用肯定不是這樣,一般是點擊一個按鈕,在不同狀態之間toggle。

為此我在上面的基礎上增加一個按鈕,然后在onclick中增加toggle的代碼,順便將BottomSheetDialogFragment的代碼也添加到MainActivity.java中:

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.collapse_bottom_sheet_button:
            // Collapsing the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            break;
        case R.id.expand_bottom_sheet_button:
            // Expanding the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            break;
        case R.id.hide_bottom_sheet_button:
            // Hiding the bottom sheet
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
            break;
        case R.id.show_bottom_sheet_dialog_button:
            // Opening the Dialog Bottom Sheet
            new CustomBottomSheetDialogFragment().show(getSupportFragmentManager(), "Dialog");
            break;
        case R.id.bottom_sheet_toggle:
            if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED ){
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
            } else if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN || bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED){
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            }

        break;

}

}</code></pre>

整個demo的代碼可以在github下載 https://github.com/jianghejie/bottom-sheet-tutorial

補充

Persistent bottom sheet xml布局中的

    app:behavior_hideable="true"
    app:behavior_peekHeight="60dp"

可以用代碼實現

mBottomSheetBehavior.setHideable(true);
mBottomSheetBehavior.setPeekHeight(300);

第三方的bottom sheet

 

來自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0327/7729.html

 

 本文由用戶 ahdubi 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!