Как имитировать поведение трех фаз нижнего листа Google Maps?

Фон

Мне поручено создать пользовательский интерфейс, который ведет себя аналогично тому, как Google Maps показывает нижний лист для найденного результата.

Он состоит из трех различных фаз:

  1. Нижнее содержимое. Верхняя область по-прежнему доступна для прикосновения, а внизу ничего не прокручивается.
  2. Полноэкранный контент, при этом в верхней части есть большой заголовок.
  3. Полноэкранный контент, а в верхней части находится только панель инструментов.

Вот о чем я говорю на Google Maps:

Введите описание изображения здесь

Эта проблема

Дело в том, что нижний лист еще не является частью библиотеки дизайна (хотя он был запрошен, здесь).

Более того, пользовательский интерфейс кажется довольно сложным и требует обработки панели инструментов на нескольких этапах.

Что я пробовал

Я нашел хорошую (достаточно) библиотеку для нижнего листа (здесь ) и добавил контент в свой образец фрагмента, чтобы иметь те же представления, что и в образцах дизайна материалов (например, < strong> здесь), чтобы иметь CollapsingToolbarLayout, который позаботится о фазах 2 + 3.

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

### fragment_my.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/main_content"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/detail_backdrop_height"

        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"

            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:paddingTop="24dp">

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Info"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title"/>

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/cheese_ipsum"/>
                </LinearLayout>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/card_margin"
                android:layout_marginLeft="@dimen/card_margin"
                android:layout_marginRight="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Friends"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title"/>

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/cheese_ipsum"/>
                </LinearLayout>
            </android.support.v7.widget.CardView>

            <android.support.v7.widget.CardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/card_margin"
                android:layout_marginLeft="@dimen/card_margin"
                android:layout_marginRight="@dimen/card_margin">

                <LinearLayout
                    style="@style/Widget.CardContent"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Related"
                        android:textAppearance="@style/TextAppearance.AppCompat.Title"/>

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="@string/cheese_ipsum"/>
                </LinearLayout>
            </android.support.v7.widget.CardView>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:clickable="true"
        android:src="@android:drawable/ic_menu_send"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"/>

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

### MyFragment.java

public class MyFragment extends BottomSheetFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.fragment_my, container, false);
        view.setMinimumHeight(getResources().getDisplayMetrics().heightPixels);
        CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsing_toolbar);
        collapsingToolbar.setTitle("AAA");
        final Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
        final AppCompatActivity activity = (AppCompatActivity) getActivity();
        activity.setSupportActionBar(toolbar);
        activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        //toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                NavUtils.navigateUpFromSameTask(getActivity());
            }
        });
        final ImageView imageView = (ImageView) view.findViewById(R.id.backdrop);

        Glide.with(this).load(R.drawable.cheese_1).centerCrop().into(imageView);
        return view;
    }
}

### BottomSheetFragmentActivity.java

public final class BottomSheetFragmentActivity extends AppCompatActivity {

    protected BottomSheetLayout bottomSheetLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_sheet_fragment);
        bottomSheetLayout = (BottomSheetLayout) findViewById(R.id.bottomsheet);
        findViewById(R.id.bottomsheet_fragment_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new MyFragment().show(getSupportFragmentManager(), R.id.bottomsheet);
            }
        });
        bottomSheetLayout.setShouldDimContentView(false);
        bottomSheetLayout.setPeekOnDismiss(true);
        bottomSheetLayout.setPeekSheetTranslation(200);
        bottomSheetLayout.setInterceptContentTouch(false);
        bottomSheetLayout.setDefaultViewTransformer(new BaseViewTransformer() {
            @Override
            public void transformView(final float translation, final float maxTranslation, final float peekedTranslation, final BottomSheetLayout parent, final View view) {
                Log.d("AppLog", "translation:" + translation + " maxTranslation:" + maxTranslation + " peekedTranslation:" + peekedTranslation);
            }
        });
    }
}

Работает почти хорошо. Единственная проблема - это переход с №3 обратно на №2:

введите описание изображения здесь

Вопрос

Что не так с кодом? Что я могу сделать, чтобы добиться требуемого поведения?


comment
Для меня это похоже на переходы активности. Вы пробовали создать 2 Activity и применить материальные переходы между ними? А использовали CoordinatorLayout на 2-м экране?   -  person S.D.    schedule 17.12.2015
comment
@ S.D. Я уверен, что это не 2 действия, потому что вы можете продолжать касаться экрана, чтобы прокручивать и переключаться между фазами. Это не мешает вам перейти к следующему / предыдущему занятию. При открытии нового действия я не думаю, что возможно сохранить одни и те же события касания для механизма прокрутки. Я даже не уверен, можно ли использовать фрагменты, но это, вероятно, более вероятно, чем действия.   -  person android developer    schedule 17.12.2015
comment
Тогда я думаю, что все представления имеют один и тот же макет, каждый из которых имеет определенный Поведение. И все действия запускаются перехватом вертикальной прокрутки в корневом макете, который все координирует.   -  person S.D.    schedule 17.12.2015
comment
@ S.D. Вы знаете, как заставить его работать? Это лучше, чем я нашел?   -  person android developer    schedule 17.12.2015
comment
Я думаю, вам стоит взглянуть на эту библиотеку.   -  person Savelii Zagurskii    schedule 26.01.2016
comment
@SaveliiZagurskii А вы пробовали добавить то, что я написал, в образец этой библиотеки?   -  person android developer    schedule 27.01.2016


Ответы (4)


Примечание. Прочтите правки внизу


Хорошо, я нашел способ сделать это, но мне пришлось изменить код нескольких классов, чтобы нижний лист знал о состоянии appBarLayout (развернутый или нет) и игнорировал прокрутку вверх, если это не раскрывается:

BottomSheetLayout.java

Добавлены поля:

private AppBarLayout mAppBarLayout;
private OnOffsetChangedListener mOnOffsetChangedListener;
private int mAppBarLayoutOffset;

init () - добавил это:

    mOnOffsetChangedListener = new OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(final AppBarLayout appBarLayout, final int verticalOffset) {
            mAppBarLayoutOffset = verticalOffset;
        }
    };

Добавлена ​​функция для установки appBarLayout:

public void setAppBarLayout(final AppBarLayout appBarLayout) {
    if (mAppBarLayout == appBarLayout)
        return;
    if (mAppBarLayout != null)
        mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);
    mAppBarLayout = appBarLayout;
    mAppBarLayout.addOnOffsetChangedListener(mOnOffsetChangedListener);
}

onDetachedFromWindow () - добавил это:

    if (mAppBarLayout != null)
        mAppBarLayout.removeOnOffsetChangedListener(mOnOffsetChangedListener);

onTouchEvent () - добавил это:

      ...
      if (bottomSheetOwnsTouch) {
        if (state == State.EXPANDED && scrollingDown && mAppBarLayout != null && mAppBarLayoutOffset != 0) {
            event.offsetLocation(0, sheetTranslation - getHeight());
            getSheetView().dispatchTouchEvent(event);
            return true;
        }
      ...

Это были основные изменения. Теперь о том, что их задает:

MyFragment.java

onCreateView () - добавил это:

    mBottomSheetLayout.setAppBarLayout((AppBarLayout) view.findViewById(R.id.appbar));

Я также добавил эту функцию:

 public void setBottomSheetLayout(final BottomSheetLayout bottomSheetLayout) {
    mBottomSheetLayout = bottomSheetLayout;
}

Вот как активность сообщает фрагменту о appBarLayout:

            final MyFragment myFragment = new MyFragment();
            myFragment.setBottomSheetLayout(bottomSheetLayout);
            myFragment.show(getSupportFragmentManager(), R.id.bottomsheet);

Проект теперь доступен на GitHub:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet.

Надеюсь, в нем нет ошибок.


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

  1. Хорошо работает только на Android 6 и выше. У других странное поведение: нижний лист раскрывается на крошечную долю времени каждый раз при его отображении.
  2. Изменение ориентации вообще не сохраняет состояние прокрутки, поэтому я отключил ее.
  3. Редкая проблема с возможностью прокрутки содержимого нижнего листа, пока он все еще свернут (внизу)
  4. Если клавиатура была показана раньше, нижний лист может выходить в полноэкранный режим при попытке заглянуть.

Если кто-то может помочь с этим, пожалуйста.


Для проблемы №1 я попытался добавить исправление, установив видимость НЕВИДИМЫЙ, когда нижний лист еще не просматривается, но это не всегда работает, особенно если отображается клавиатура.


Что касается проблемы №1, я нашел, как ее исправить, просто обернув (в fragment_my.xml) CoordinatorLayout любым представлением, которое вы хотите использовать (я использовал FrameLayout), а также поместил в него полноразмерное представление ( Я просто поставил View), как таковой:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--This full sized view, together with the FrameLayout above, are used to handle some weird UI issues on pre-Android-6 -->
    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <...CollapsingToolbarLayout 
    ...

Вероятно, это сбило с толку bottomSheet, когда у меня был CoordinatorLayout в качестве его представления. Я обновил проект, но, тем не менее, если есть способ найти более приятное решение, я хотел бы знать об этом.


В последние месяцы Google опубликовал свой собственный класс bottomSheet, но, как я обнаружил, у него много проблем, поэтому я даже не могу его попробовать.

person android developer    schedule 09.12.2015
comment
а как насчет этого изображения? cloud.githubusercontent.com/assets/5357526/ 11641271 / я хочу реализовать этот вид слайдов изображений вместе с нижним листом вверх - person Hardy; 16.12.2015
comment
@HBdroid Думаю, это возможно. может быть, для функции onOffsetChanged также изменить перевод mBottomSheetBackgroundImageView? В моем случае сначала требовалось обрабатывать 3 фазы. Теперь вопрос в том, что и как переходить, и это очень индивидуально и зависит от ваших потребностей. Это также требует утомительной математики. Я предлагаю использовать функцию мгновенного запуска, чтобы быстро попробовать. - person android developer; 16.12.2015
comment
я не могу найти решение, пожалуйста, помогите мне - person Hardy; 16.12.2015
comment
@HBdroid Извините, я не знаю. Попробуйте удалить это приложение: layout_collapseMode = parallax. также попробуй сделать перевод в transformView. - person android developer; 16.12.2015
comment
@ Харди, вы в итоге создали то решение, которое хотели? если да, то это открытый исходный код и можно ли им поделиться? - person N Jay; 10.04.2016
comment
@NJay Я знаю, что мой ответ довольно длинный, но репозиторий github есть ... :) Здесь: github.com / AndroidDeveloperLB / ThreePhasesBottomSheet - person android developer; 10.04.2016
comment
Спасибо, уже получил ваш код. охлаждает действительно здорово и ценю, что вы открыли его исходный код. Хотя это большой багги. если вы перейдете из состояния 2, например, изменения состояния не будут отображаться панели инструментов расширенного представления. Мне просто было интересно, было ли готовое стабильное решение, прежде чем я закончу использовать ваше. @androiddeveloper - person N Jay; 10.04.2016
comment
@NJay Да, я знаю. Простите за это. Если вы знаете, как это исправить, сообщите мне об этом. - person android developer; 10.04.2016

БОЛЬШОЕ ОБНОВЛЕНИЕ

Потому что было около 4 или 5 вопросов по одной и той же теме, НО с РАЗНЫМИ требованиями, и я попытался ответить на все из них, но невежливый администратор удалил / закрыл их, заставив меня создать заявку для каждого и изменить их на избегайте копирования и вставки, я дам вам ссылку на полный ответ, где вы можете найти все объяснения о том, как получить полное поведение как Google Maps.


Отвечая на ваш вопрос

Как имитировать поведение трех фаз нижнего листа Google Maps?

С помощью библиотеки поддержки 23.x.x + вы можете сделать это, изменив значение по умолчанию BottomSheetBehavior, добавив еще один стат, выполнив следующие действия:

  1. Создайте класс Java и расширьте его с CoordinatorLayout.Behavior<V>

  2. Скопируйте и вставьте код из BottomSheetBehavior файла по умолчанию в новый.

  3. Измените метод clampViewPositionVertical следующим кодом:

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
    }
    int constrain(int amount, int low, int high) {
        return amount < low ? low : (amount > high ? high : amount);
    }
    
  4. Добавить новое состояние:

    public static final int STATE_ANCHOR_POINT = X;
    
  5. Измените следующие методы: onLayoutChild, onStopNestedScroll, BottomSheetBehavior<V> from(V view) и setState (необязательно)

Я собираюсь добавить эти измененные методы и ссылку на пример проекта.

А вот как это выглядит
CustomBottomSheet /></p>
                        <div class= person MiguelHincapieC    schedule 25.05.2016

comment
Я протестировал репозиторий github сейчас, и это кажется приятным. Но верхняя синяя область иногда выглядит неполной. Кроме того, я не могу понять, где обрабатывать представления, которые необходимо перемещать при перетаскивании нижнего листа. В сделанном мной репо (здесь: github.com/AndroidDeveloperLB/ThreePhasesBottomSheet) изображение тускнеет , и маленькое изображение перемещается из одного места в другое, включая изменение своего размера. Я хотел бы знать, где добавить их обработку. - person android developer; 25.05.2016
comment
Привет, у меня есть локальная версия с изображением параллакса, но она еще не работает (я могу нажать ее, если вы хотите ее посмотреть). Вы можете добавить любой вид внутри CoordinatorLayout в activity_main.xml. Думаю, у вас есть хороший опыт работы с макетом координатора, в противном случае взгляните на эту ссылку, которую я нашел - person MiguelHincapieC; 25.05.2016
comment
Я собираюсь посмотреть, как вы добились поведения панели инструментов, и буду использовать его на моем: D. Кстати, есть небольшое поведение, которое я мог бы только имитировать, если использую библиотеку поддержки 23.2: на картах Google, если вы перетащите изображение, которое ниже панели инструментов, оно переместит нижний лист, но если вы обновитесь до 23.4 или minSdkVersion 14+, вы будете потерять это поведение o_O ' - person MiguelHincapieC; 25.05.2016
comment
@androiddeveloper Я понял! теперь он работает с эффектом параллакса изображения и анимацией панелей инструментов ... Мне не хватает только цвета, который принимает панель инструментов, когда вы продолжаете скользить вверх: D - person MiguelHincapieC; 26.05.2016
comment
@MiguelHincapieC Привет, я хочу отображать только основную панель инструментов, и я удалил объединенный макет панели приложений, но на нижнем листе разверните строку состояния, не отображающуюся, и конец изображения паралллекса до позиции строки состояния, и я хочу, чтобы изображение паралллекса было прикреплено под основной панелью инструментов. можешь объяснить, как я могу это сделать? - person Vijay Rajput; 07.11.2017

Мне также пришлось реализовать представление, подобное тому, как Google Maps показывает нижний лист для найденного результата.

Вот как выглядит мой:

Обзор

Развернуть вид с прокруткой вверх

Развернуть представление с прокруткой вниз

Сначала я определил нижний лист с заголовком и прокручиваемым содержимым, но layout_height, похоже, не обернул содержимое ни заголовок, ни прокручиваемое содержимое, несмотря на указание wrap_content.

Эта проблема исчезла, когда я использовал LinearLayout вместо ConstraintLayout для дочернего макета CoordinatorLayout (и для его дочерних элементов).

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

    <Button
        android:id="@+id/buttonPeek"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Peek"
        app:layout_constraintEnd_toStartOf="@+id/buttonExpand"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/buttonExpand"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Expand"
        app:layout_constraintEnd_toStartOf="@+id/buttonClose"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/buttonPeek"
        app:layout_constraintTop_toTopOf="@+id/buttonPeek" />

    <Button
        android:id="@+id/buttonClose"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Close"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/buttonExpand"
        app:layout_constraintTop_toTopOf="@+id/buttonExpand" />

    <androidx.coordinatorlayout.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:id="@+id/layout_coordinator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:id="@+id/layout_coordinator_child"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:behavior_hideable="true"
            app:layout_behavior="@string/bottom_sheet_behavior">

            <LinearLayout
                android:id="@+id/layout_bottom_sheet_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#FFFF0000"
                android:orientation="vertical" >

                <TextView
                    android:id="@+id/headerTextView_a"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="a" />

                <TextView
                android:id="@+id/headerTextView_b"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="b" />

                <TextView
                android:id="@+id/headerTextView_c"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="c" />

                <TextView
                android:id="@+id/headerTextView_d"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="d" />

                <TextView
                android:id="@+id/headerTextView_e"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="e" />

                <TextView
                android:id="@+id/headerTextView_f"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="f" />

                <TextView
                android:id="@+id/headerTextView_g"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="g" />

                <TextView
                android:id="@+id/headerTextView_h"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="h" />

                <TextView
                android:id="@+id/headerTextView_i"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="i" />

                <TextView
                android:id="@+id/headerTextView_j"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="j" />

                <TextView
                android:id="@+id/headerTextView_k"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="k" />

            </LinearLayout>

            <androidx.core.widget.NestedScrollView
                android:id="@+id/layout_bottom_sheet_scrollable_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="#FF00FF00"
                android:fillViewport="true" >

                <LinearLayout
                    android:id="@+id/layout_bottom_sheet_scrollable_content"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/textView0"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="0" />

                    <TextView
                        android:id="@+id/textView1"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="1" />

                    <TextView
                        android:id="@+id/textView2"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="2" />

                    <TextView
                        android:id="@+id/textView3"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="3" />

                    <TextView
                        android:id="@+id/textView4"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="4" />

                    <TextView
                        android:id="@+id/textView5"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="5" />

                    <TextView
                        android:id="@+id/textView6"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="6" />

                    <TextView
                        android:id="@+id/textView7"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="7" />

                    <TextView
                        android:id="@+id/textView8"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="8" />

                    <TextView
                        android:id="@+id/textView9"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="9" />

                    <TextView
                        android:id="@+id/textView10"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="10" />

                    <TextView
                        android:id="@+id/textView11"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="11" />

                    <TextView
                        android:id="@+id/textView12"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="12" />

                    <TextView
                        android:id="@+id/textView13"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="13" />

                    <TextView
                        android:id="@+id/textView14"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="14" />

                    <TextView
                        android:id="@+id/textView15"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="15" />

                    <TextView
                        android:id="@+id/textView16"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="16" />

                    <TextView
                        android:id="@+id/textView17"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="17" />

                    <TextView
                        android:id="@+id/textView18"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="18" />

                    <TextView
                        android:id="@+id/textView19"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="19" />

                    <TextView
                        android:id="@+id/textView20"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="20" />

                    <TextView
                        android:id="@+id/textView21"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="21" />

                    <TextView
                        android:id="@+id/textView22"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="22" />

                    <TextView
                        android:id="@+id/textView23"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="23" />

                    <TextView
                        android:id="@+id/textView24"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="24" />

                    <TextView
                        android:id="@+id/textView25"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="25" />

                    <TextView
                        android:id="@+id/textView26"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="26" />

                    <TextView
                        android:id="@+id/textView27"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="27" />

                    <TextView
                        android:id="@+id/textView28"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="28" />

                    <TextView
                        android:id="@+id/textView29"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="29" />

                    <TextView
                        android:id="@+id/textView30"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="30" />

                    <TextView
                        android:id="@+id/textView31"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="31" />

                    <TextView
                        android:id="@+id/textView32"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="32" />

                    <TextView
                        android:id="@+id/textView33"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="33" />

                    <TextView
                        android:id="@+id/textView34"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="34" />

                    <TextView
                        android:id="@+id/textView35"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="35" />

                    <TextView
                        android:id="@+id/textView36"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="36" />

                    <TextView
                        android:id="@+id/textView37"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="37" />

                    <TextView
                        android:id="@+id/textView38"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="38" />

                    <TextView
                        android:id="@+id/textView39"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="39" />

                    <TextView
                        android:id="@+id/textView40"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="40" />

                    <TextView
                        android:id="@+id/textView41"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="41" />

                    <TextView
                        android:id="@+id/textView42"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="42" />

                    <TextView
                        android:id="@+id/textView43"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="43" />

                    <TextView
                        android:id="@+id/textView44"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="44" />

                    <TextView
                        android:id="@+id/textView45"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="45" />

                    <TextView
                        android:id="@+id/textView46"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="46" />

                    <TextView
                        android:id="@+id/textView47"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="47" />

                    <TextView
                        android:id="@+id/textView48"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="48" />

                    <TextView
                        android:id="@+id/textView49"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="49" />

                </LinearLayout>

            </androidx.core.widget.NestedScrollView>
        </LinearLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java

package com.example.bottomsheetwithscrollablecontent;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.google.android.material.bottomsheet.BottomSheetBehavior;

import androidx.appcompat.app.AppCompatActivity;
import androidx.coordinatorlayout.widget.CoordinatorLayout;

public class MainActivity extends AppCompatActivity {
    private CoordinatorLayout layout_coordinator;
    private View layout_coordinator_child;
    private View layout_bottom_sheet_header;

    private BottomSheetBehavior behavior;

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

        layout_coordinator = findViewById(R.id.layout_coordinator);
        layout_coordinator_child = layout_coordinator.findViewById(R.id.layout_coordinator_child);
        layout_bottom_sheet_header = layout_coordinator.findViewById(R.id.layout_bottom_sheet_header);

        behavior = BottomSheetBehavior.from(layout_coordinator_child);

        Button buttonPeek = findViewById(R.id.buttonPeek);
        buttonPeek.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                behavior.setPeekHeight(layout_bottom_sheet_header.getHeight());
                behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
            }
        });

        Button buttonExpand = findViewById(R.id.buttonExpand);
        buttonExpand.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

        Button buttonClose = findViewById(R.id.buttonClose);
        buttonClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                behavior.setState(BottomSheetBehavior.STATE_HIDDEN);
            }
        });
    }
}

приложение / build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.bottomsheetwithscrollablecontent"
        minSdkVersion 24
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0-beta01'
    implementation "com.google.android.material:material:1.1.0-alpha04"
}
person Michael Osofsky    schedule 22.03.2019

Вы пробовали это? http://android-developers.blogspot.in/2016/02/android-support-library-232.html?m=1 Здесь говорится, что мы можем просто указать поведение макета нижнего листа.

ОБНОВЛЕНИЕ:

По сути, ссылка гласит:

Прикрепив BottomSheetBehavior к дочернему View CoordinatorLayout (т. Е. Добавив app: layout_behavior = android.support.design.widget.BottomSheetBehavior), вы автоматически получите соответствующее обнаружение касания для перехода между пятью состояниями:

STATE_COLLAPSED: this collapsed state is the default and shows just a portion of the layout along the bottom. The height can be controlled with the app:behavior_peekHeight attribute (defaults to 0)
STATE_DRAGGING: the intermediate state while the user is directly dragging the bottom sheet up or down
STATE_SETTLING: that brief time between when the View is released and settling into its final position
STATE_EXPANDED: the fully expanded state of the bottom sheet, where either the whole bottom sheet is visible (if its height is less than the containing CoordinatorLayout) or the entire CoordinatorLayout is filled
STATE_HIDDEN: disabled by default (and enabled with the app:behavior_hideable attribute), enabling this allows users to swipe down on the bottom sheet to completely hide the bottom sheet
Keep in mind that scrolling containers in your bottom sheet must support nested scrolling (for example, NestedScrollView, RecyclerView, or ListView/ScrollView on API 21+).

Если вы хотите получать обратные вызовы об изменении состояния, вы можете добавить BottomSheetCallback:

// The View with the BottomSheetBehavior  
 View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);  
 BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);  
 behavior.setBottomSheetCallback(new BottomSheetCallback() {  
    @Override  
    public void onStateChanged(@NonNull View bottomSheet, int newState) {  
      // React to state change  
    }  
      @Override  
      public void onSlide(@NonNull View bottomSheet, float slideOffset) {  
       // React to dragging events  
   }  
 });  

Хотя BottomSheetBehavior фиксирует постоянный регистр нижнего листа, этот выпуск также предоставляет BottomSheetDialog и BottomSheetDialogFragment для заполнения варианта использования модальных нижних листов. Просто замените AppCompatDialog или AppCompatDialogFragment их эквивалентами нижнего листа, чтобы ваш диалог был стилизован как нижний лист.

person Vaibhav Sharma    schedule 04.05.2016
comment
Вопрос был задан до того, как Google показал свой класс библиотеки поддержки. Если у вас есть работающее решение с его использованием, покажите его здесь. - person android developer; 04.05.2016
comment
@androiddeveloper Я не читал дату вопроса и поэтому предложил этот ответ. Но если вы хотите использовать эту библиотеку, вы можете ее использовать. Что касается рабочего решения для этого кода, то у меня его нет. Извините. - person Vaibhav Sharma; 04.05.2016