Bagaimana cara meniru perilaku fase 3 lembar bawah Google Maps?

110

Latar Belakang

Saya ditugaskan untuk membuat UI yang berperilaku mirip dengan bagaimana Google Maps menampilkan lembaran bawah untuk hasil yang ditemukan.

Ini memiliki tiga fase berbeda:

  1. Konten bawah. Area atas masih dapat disentuh dan tidak akan menggulir apa pun di bagian bawah
  2. Konten layar penuh, sedangkan area atas memiliki header berukuran besar.
  3. Konten layar penuh, sedangkan area atas hanya memiliki toolbar.

Inilah yang saya bicarakan di Google Maps:

Masukkan deskripsi gambar di sini

Masalah

Masalahnya, lembar bawah belum menjadi bagian dari pustaka desain (meskipun diminta, di sini ).

Tidak hanya itu, UI-nya terlihat cukup rumit dan membutuhkan penanganan toolbar pada beberapa fase.

Apa yang saya coba

Saya telah menemukan pustaka (cukup) yang bagus untuk lembar bawah (di sini ), dan menambahkan konten ke sampel fragmennya, untuk memiliki tampilan yang hampir sama seperti yang ditunjukkan pada sampel desain material (seperti di sini ), untuk memiliki CollapsingToolbarLayout yang akan menangani dari fase 2 + 3.

Dalam aplikasi yang saya buat, saya juga harus memindahkan ikon saat Anda menggulir, tetapi saya rasa jika saya berhasil dengan yang lainnya, ini akan mudah. Berikut kodenya:

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);
            }
        });
    }
}

Ini hampir bekerja dengan baik. Satu-satunya masalah adalah transisi dari # 3 kembali ke # 2:

masukkan deskripsi gambar di sini

Pertanyaan

Apa yang salah dengan kode tersebut? Apa yang dapat saya lakukan untuk mencapai perilaku yang diperlukan?

pengembang android
sumber
Sepertinya Transisi Aktivitas bagi saya. Sudahkah Anda mencoba membuat 2 Aktivitas dan menerapkan transisi material di antara keduanya? Dan digunakan CoordinatorLayoutdi layar ke-2?
SD
@SD Saya sangat yakin ini bukan 2 kegiatan, karena Anda dapat terus menyentuh layar untuk menggulir dan beralih antar fase. Itu tidak menghentikan Anda untuk pergi ke aktivitas berikutnya / sebelumnya. Saat membuka aktivitas baru, menurut saya tidak mungkin untuk mempertahankan kejadian sentuh yang sama untuk mekanisme pengguliran. Saya bahkan tidak yakin apakah itu mungkin menggunakan fragmen, tetapi ini mungkin lebih mungkin daripada aktivitas.
Pengembang android
Lalu saya pikir semua tampilan berada dalam tata letak yang sama, dengan masing-masing memiliki set Perilaku tertentu di atasnya. Dan semua perilaku dipicu dari intersepsi gulir vertikal pada tata letak root yang mengoordinasikan segalanya.
SD
@SD Apakah Anda tahu bagaimana membuatnya bekerja dengan baik? Apakah lebih baik dari yang saya temukan?
Pengembang android
1
Saya pikir Anda harus melihat perpustakaan ini .
Savelii Zagurskii

Jawaban:

18

Catatan : Baca edit di bagian bawah


Oke, saya telah menemukan cara untuk melakukannya, tetapi saya harus mengubah kode dari beberapa kelas, sehingga lembar bawah akan mengetahui status appBarLayout (diperluas atau tidak), dan mengabaikan scroll-up jika tidak diperluas:

BottomSheetLayout.java

Bidang yang ditambahkan:

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

init () - menambahkan ini:

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

Menambahkan fungsi untuk mengatur appBarLayout:

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

onDetachedFromWindow () - menambahkan ini:

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

onTouchEvent () - menambahkan ini:

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

Itu adalah perubahan utama. Sekarang untuk apa yang membuat mereka:

MyFragment.java

onCreateView () - menambahkan ini:

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

Saya juga menambahkan fungsi ini:

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

Inilah cara aktivitas memberi tahu fragmen tentang appBarLayout:

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

Proyek sekarang tersedia di GitHub:

https://github.com/AndroidDeveloperLB/ThreePhasesBottomSheet

Saya harap tidak ada bug.


Solusinya memiliki bug, sayangnya, jadi saya tidak akan menandai jawaban ini sebagai yang benar:

  1. Ini hanya berfungsi dengan baik di Android 6 dan di atasnya. Yang lain memiliki perilaku aneh dengan menunjukkan lembaran bawah yang diperluas untuk sebagian kecil waktu, setiap kali saat menampilkannya.
  2. Perubahan orientasi sama sekali tidak menyimpan status pengguliran, jadi saya telah menonaktifkannya.
  3. Masalah langka untuk dapat menggulir di dalam konten lembar bawah saat masih tertutup (di bagian bawah)
  4. Jika keyboard telah ditunjukkan sebelumnya, lembar bawah mungkin menjadi layar penuh saat mencoba untuk mengintip.

Jika ada yang bisa membantu, tolong lakukan.


Untuk masalah # 1, saya telah mencoba menambahkan perbaikan dengan mengatur visibilitas ke TIDAK TERLIHAT ketika lembar bawah belum diintip, tetapi tidak selalu berfungsi, terutama jika keyboard ditampilkan.


Untuk masalah # 1, saya telah menemukan cara memperbaikinya, dengan hanya membungkus (dalam "fragment_my.xml") CoordinatorLayout dengan tampilan apa pun yang ingin Anda gunakan (saya menggunakan FrameLayout), dan juga meletakkan tampilan berukuran penuh di itu (saya hanya meletakkan "View"), seperti:

<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 
    ...

Ini mungkin membingungkan bottomSheet ketika saya melihat CoordinatorLayout sebagai tampilannya. Saya telah memperbarui proyek tersebut, tetapi tetap saja, jika ada cara untuk mendapatkan solusi yang lebih baik, saya ingin mengetahuinya.


Dalam beberapa bulan terakhir, Google telah menerbitkan kelas bottomSheet-nya sendiri, tetapi seperti yang saya temukan memiliki banyak masalah, jadi saya bahkan tidak dapat mencobanya.

pengembang android
sumber
tapi bagaimana dengan gambar ini? cloud.githubusercontent.com/assets/5357526/11641271/… saya ingin menerapkan jenis slide gambar ini bersama dengan bottomsheet naik
Hardy
@HBroid Saya pikir itu mungkin. mungkin untuk fungsi "onOffsetChanged", juga mengubah terjemahan mBottomSheetBackgroundImageView? Persyaratan pada kasus saya adalah yang pertama menangani 3 fase. Sekarang pertanyaannya tentang apa yang harus ditransisikan dan bagaimana, dan ini sangat disesuaikan dan tergantung pada kebutuhan Anda. Ini juga membutuhkan banyak matematika yang mengganggu. Saya menyarankan menggunakan fitur instant-run agar cepat mencoba.
Pengembang android
saya tidak dapat menemukan solusinya tolong bantu saya
Hardy
@HBdroid Maaf saya tidak tahu. Coba mungkin hapus aplikasi ini: layout_collapseMode = "parallax". juga mencoba melakukan terjemahan di transformView.
Pengembang android
2
@Hardy, apakah Anda akhirnya menciptakan solusi yang Anda inginkan? jika ya, apakah itu open source dan dapatkah itu dibagikan?
N Jay
15

PEMBARUAN BESAR

Karena ada 4 atau 5 pertanyaan tentang topik yang sama, TETAPI dengan persyaratan yang BERBEDA, dan saya mencoba menjawab semuanya, tetapi admin yang tidak sopan menghapus / menutupnya, membuat saya membuat tiket untuk masing-masing dan mengubahnya menjadi hindari "salin tempel" Saya akan memberi Anda tautan ke jawaban lengkap di mana Anda dapat menemukan semua penjelasan tentang cara mendapatkan perilaku lengkap seperti Google Maps.


Menjawab pertanyaan Anda

Bagaimana cara meniru perilaku fase 3 lembar bawah Google Maps?

Dengan support library 23.x.x + Anda dapat melakukannya dengan memodifikasi default BottomSheetBehavior, menambahkan satu stat lagi dengan langkah-langkah berikut:

  1. Buat kelas Java dan perpanjang dari CoordinatorLayout.Behavior<V>
  2. Salin kode tempel dari BottomSheetBehaviorfile default ke yang baru.
  3. Ubah metode clampViewPositionVerticaldengan kode berikut:

    @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. Tambahkan negara bagian baru:

    public static final int STATE_ANCHOR_POINT = X;
  5. Memodifikasi metode berikutnya: onLayoutChild, onStopNestedScroll, BottomSheetBehavior<V> from(V view)dan setState(opsional)

Saya akan menambahkan metode yang dimodifikasi tersebut dan tautan ke proyek contoh .

Dan inilah tampilannya:
CustomBottomSheetBehavior

MiguelHincapieC
sumber
Saya sudah menguji repo github sekarang, dan sepertinya bagus. Tetapi terkadang area biru atas terlihat sebagian. Selain itu, saya tidak dapat menemukan tempat untuk menangani tampilan yang perlu dipindahkan saat Anda menyeret lembar bawah. Di repo yang saya buat (di sini: github.com/AndroidDeveloperLB/ThreePhasesBottomSheet ), gambar memudar, dan gambar kecil berpindah dari satu tempat ke tempat lain, termasuk mengubah ukurannya. Saya ingin tahu di mana menambahkan penanganannya.
Pengembang android
Halo, saya memiliki versi lokal dengan gambar paralaks tetapi belum berfungsi dengan baik (saya dapat mendorongnya jika Anda ingin melihatnya). Anda dapat menambahkan tampilan apa pun di CoordinatorLayoutdalam activity_main.xml. Saya kira Anda memiliki pengalaman yang baik dengan tata letak koordinator, jika tidak lihat tautan
MiguelHincapieC
Saya akan melihat bagaimana Anda mendapatkan perilaku toolbar dan akan menggunakannya pada saya: D. Ngomong-ngomong ada sedikit perilaku yang hanya bisa saya tiru jika saya menggunakan pustaka dukungan 23.2: di google maps jika Anda menyeret gambar yang di bawah bilah alat itu akan memindahkan lembar bawah, tetapi jika Anda meningkatkan ke 23.4 atau minSdkVersion 14+ Anda akan kehilangan perilaku ini o_O '
MiguelHincapieC
@androiddeveloper Saya mengerti! sekarang bekerja dengan efek paralaks gambar dan animasi bilah alat ... Saya hanya kehilangan warna yang mengambil bilah alat saat Anda terus menggeser ke atas: D
MiguelHincapieC
@MiguelHincapieC Hai, Saya hanya ingin menampilkan toolbar utama dan saya menghapus tata letak appbar gabungan tetapi di lembar bawah perluas bilah status waktu tidak ditampilkan dan gambar lumpuh berakhir ke posisi bilah status dan saya ingin gambar lumpuh lengket di bawah bilah alat utama. dapatkah Anda menjelaskan bagaimana saya melakukannya
Vijay Rajput
0

Apakah kamu sudah mencobanya? http://android-developers.blogspot.in/2016/02/android-support-library-232.html?m=1 Di sini dikatakan bahwa kita hanya dapat menentukan perilaku tata letak lembar bawah.

MEMPERBARUI:

Pada dasarnya tautan menyatakan-

Dengan melampirkan BottomSheetBehavior ke Tampilan anak dari CoordinatorLayout (yaitu, menambahkan app: layout_behavior = "android.support.design.widget.BottomSheetBehavior"), Anda secara otomatis akan mendapatkan deteksi sentuhan yang sesuai untuk transisi antara lima keadaan:

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+).

Jika Anda ingin menerima callback dari perubahan status, Anda dapat menambahkan 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  
   }  
 });  

Sementara BottomSheetBehavior menangkap kasus lembar bawah yang persisten, rilis ini juga menyediakan BottomSheetDialog dan BottomSheetDialogFragment untuk mengisi kasus penggunaan lembar dasar modal. Cukup ganti AppCompatDialog atau AppCompatDialogFragment dengan lembar bawahnya yang setara agar dialog Anda diberi gaya sebagai lembar bawah.

Vaibhav Sharma
sumber
Pertanyaan itu dibuat sebelum Google menunjukkan kelas perpustakaan dukungan mereka. Jika Anda memiliki solusi yang berfungsi menggunakannya, tunjukkan di sini.
Pengembang android
@androiddeveloper Saya tidak membaca tanggal pertanyaan dan karenanya saya menyarankan jawaban ini. Tetapi jika Anda ingin menggunakan perpustakaan ini, Anda dapat menggunakannya. Sejauh solusi yang berfungsi untuk kode ini, saya tidak memilikinya. Maaf.
Vaibhav Sharma
0

Saya juga harus menerapkan tampilan yang mirip dengan cara Google Maps menampilkan lembaran bawah untuk hasil yang ditemukan.

Begini penampilan saya:

Tampilan intip

Luaskan tampilan dengan menggulir ke atas

Luaskan tampilan digulir ke bawah

Pada awalnya, saya mendefinisikan lembar bawah dengan header dan konten yang dapat digulir tetapi layout_height tampaknya tidak membungkus konten baik dari header maupun konten yang dapat digulir meskipun ditentukan wrap_content.

Masalah itu hilang ketika saya menggunakan LinearLayoutalih-alih ConstraintLayoutuntuk CoordinatorLayouttata letak anak (dan untuk anaknya).

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);
            }
        });
    }
}

app / 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"
}
Michael Osofsky
sumber