Данная статья является переводом статьи Emrullah Luleci, а также её продолжения.

Нижний экран (Здесь и далее под «нижним экраном/слоем» будет подразумеваться элемент bottom sheet — прим. пер.) — компонент, выезжающий снизу экрана, который используется для отображения дополнительного контента. Подробнее об этом элементе можно узнать на официальной сайте посвященном материальному дизайну.

image


Зависимости


Для использования этого элемента, добавьте последние версии библиотек поддержки в свой проект:

dependencies {
    //замените X.X.X номером последней версии
    compile 'com.android.support:appcompat-v7:X.X.X'
    compile 'com.android.support:design:X.X.X'
}

Создайте класс наследник от AppCompatActivity:

public class ButtonActivity extends AppCompatActivity {
...
}

Создание макетов


Содержимое нижнего экрана

Для удобства воспользуемся макетами. Назовем файл с нижним слоем bottom_sheet.xml.

bottom_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="340dp"
    android:background="@android:color/darker_gray"
    android:orientation="vertical"
    app:behavior_hideable="true"
    app:behavior_peekHeight="80dp"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="@string/bottom_sheet_peek"
        android:textColor="@android:color/white" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/bottom_sheet_content"
        android:textColor="@android:color/white" />

</LinearLayout>


behavior_peekHeight: Определяет высоту видимой части.

behavior_hideable: Определяет, может ли нижний экран скрываться свайпом вниз.

Container view


Создайте CoordinatorLayout в качестве корневого вью. Добавьте в него прямым наследником bottom_sheet.xml. Элементы app_bar и activity_bottom_sheet_content не имеют прямого отношения к нижнему экрану, поэтому их можно заменить или удалить.

Макет
<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context="com.androidsample.BottomSheetActivity">

    <!-- подключение элемента app bar -->
    <include layout="@layout/app_bar" />

    <!-- подключение основного контента -->
    <include layout="@layout/activity_bottom_sheet_content" />

    <!-- подключение нижнего экрана -->
    <include layout="@layout/bottom_sheet" />

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


На данном этапе нижний экран должен работать примерно так:

image

Динамическое управление


Поведением и свойствами нижнего экрана можно также управлять динамически с помощью Java.

Спойлер

// получение вью нижнего экрана
LinearLayout llBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);

// настройка поведения нижнего экрана
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(llBottomSheet);

// настройка состояний нижнего экрана
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);

// настройка максимальной высоты
bottomSheetBehavior.setPeekHeight(340);

// настройка возможности скрыть элемент при свайпе вниз
bottomSheetBehavior.setHideable(false);

// настройка колбэков при изменениях
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        
    }

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

    }
});


Прикрепление элементов к нижнему экрану


Также можно прикрепить вью к нижнему экрану, чтобы прикрепленный элемент перемещался одновременно с нижним слоем.

image

Добавим Floating Action Button в макет созданный выше. Новый компонент должен являться непосредственным наследником CoordinatorLayout также как и bottom_sheet. Для прикрепления элемента к нижнему экрану необходимо добавить app:layout_anchor с id вью нижнего экрана, а также app:layout_anchorGravity со значением top|end.

Макет
<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context="com.androidsample.BottomSheetActivity">

    <!-- подключение элемента app bar -->
    <include layout="@layout/app_bar" />

    <!-- подключение основного контента -->
    <include layout="@layout/activity_bottom_sheet_content" />

    <!-- подключение нижнего экрана -->
    <include layout="@layout/bottom_sheet" />

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/activity_vertical_margin"
        android:src="@drawable/ic_add_shopping_cart_white_24dp"
        android:theme="@style/PrimaryActionButton"
        app:layout_anchor="@+id/bottom_sheet"
        app:layout_anchorGravity="top|end" />

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


Теперь плавающая кнопка закреплена в верхнем углу нашего нижнего экрана и перемещается вместе с ним.

Скрытие плавающей кнопки при скроле


Для скрытия кнопки при скроле необходимо добавить слушатель к нижнему экрану и отображать/скрывать кнопку. Для начала найдем необходимые вью:

Спойлер

fab = findViewById(R.id.fab);
View llBottomSheet = findViewById(R.id.bottom_sheet);

// настройка поведения нижнего экрана
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(llBottomSheet);

Если хотите, чтоб кнопка масштабировалась во время скрола, используйте это:

// настройка колбэков при изменениях
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        fab.animate().scaleX(1 - slideOffset).scaleY(1 - slideOffset).setDuration(0).start();
    }
});


Для скрытия кнопки в момент начала скрола и отображения после полного сворачивания нижнего экрана, используйте следующее:

Спойлер

// настройка колбэков при изменениях
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {

        // этот код скрывает кнопку сразу же
	// и отображает после того как нижний экран полностью свернется
        if (BottomSheetBehavior.STATE_DRAGGING == newState) {
            fab.animate().scaleX(0).scaleY(0).setDuration(300).start();
        } else if (BottomSheetBehavior.STATE_COLLAPSED == newState) {
            fab.animate().scaleX(1).scaleY(1).setDuration(300).start();
        }
    }

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

Результат обоих вариантов можно увидеть ниже:

image

Вот и всё!
Поделиться с друзьями
-->

Комментарии (7)


  1. izzholtik
    05.09.2016 14:17
    -1

    А как предполагается доставать этот элемент после полного задвигания под панель навигации, которое видно в конце гифок?


    1. GreenNick
      05.09.2016 14:42

      Думаю, что програмно или по какому-нибудь событию


    1. ArtiomCX75
      05.09.2016 14:51
      +1

      Можно программно установить состояние нижнего экрана, например:

      bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
      

      Возможные варианты: STATE_COLLAPSED, STATE_EXPANDED, STATE_HIDDEN.


    1. Agrass
      05.09.2016 23:28

      ```java
      // настройка максимальной высоты
      bottomSheetBehavior.setPeekHeight(340);
      ```

      Автор заботливо оставил комментарий, я думаю если не указывать этот параметр, то шторка будет открываться во весь экран :)


      1. StanZakharov
        07.09.2016 17:56

        Из-за этого параметра в коде шторка не закрывается полностью. Поэтому его вообще лучше не юзать.


  1. ittakir
    05.09.2016 16:57

    В новом мобильном 2ГИС есть такая панелька.
    Бесит неимоверно тем, что анимацию сдвига они считают самостоятельно и короткое резкое движение пальцем ни к чему не приводит (панелька прыгает на пару пикселей и возвращается назад).
    Хотя, если она будет работать также, как и весь остальной UI Android (верхняя панель, разблокировка экрана), то может и нормально будет.


    1. andreich
      06.09.2016 11:13

      боюсь, что в 2гис они полностью все сами писали, потому что там QT используется