کار با Bottom Sheet متریال دیزاین
معرفی Bottom Sheet:
به نام خدا. Bottom Sheet یکی دیگر از کامپوننتهای متریال دیزاین در اندروید است که برای نمایش اطلاعات، منو ها و یا هر چیز دیگری بکار میرود. به تعریف ساده، یک صفحه یا نواری است که از پایین صفحه نمایش به سمت بالا باز میشود. به طور کلی Bottom Sheet ها را میتوان به دو دسته Persistent Bottom Sheet و Modal Bottom Sheet تقسیم کرد.
در ادامه دو مثال از کاربرد این کامپوننت را نشان میدهم که تصویر اول مربوط به Google Maps و تصویر دوم Google Drive است:
در Google Maps با Pin کردن یک نقطه روی نقشه، یک Bottom Sheet از پایین صفحه باز میشود که حاوی اطلاعات مختصری از مکان انتخاب شده است. سپس با لمس دکمه MORE INFO یا کشیدن نوار به سمت بالا، اطلاعات بیشتری به کاربر ارائه میشود که در گوگل مپ، در این حالت تمام صفحه را دربر میگیرد.
در گوگل درایو هم از این کامپوننت برای نمایش گزینههای مرتبط با فایل و همچنین سایر موارد مانند ساخت فولدر جدید یا آپلود فایل استفاده شده است.
من این مبحث را در قالب چند پروژه تهیه میکنم تا درک جزئیات و تفاوتها ساده تر شود.
ابتدا یک پروژه جدید با نام SimpleBottomSheet و یک Empty Activity ایجاد میکنم. برای پیاده سازی Bottom Sheet لازم است کتابخانه Support Design را به پروژه اضافه کنیم:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:design:27.1.1' }
در این پروژه همانطور که از نام آن پیداست قصد دارم یک Bottom Sheet را در ساده ترین حالت آن معرفی کنم. بنابراین در این مثال فقط با layout اکتیویتی سروکار دارم و از Java استفاده نخواهم کرد.
در قدم اول جهت نمایش Bottom Sheet در یک اکتیویتی، باید Root Layout را از نوع CoordinatorLayout انتخاب کنیم:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> </android.support.design.widget.CoordinatorLayout>
همانطور که در کد بالا ملاحظه میکنید، CoordinatorLayout هم از زیرمجموعه های کتابخانه Design است. از CoordinatorLayout برای کنترل حرکتها در متریال استفاده میشود. مانند اجرای animation در حرکت عناصر و المانهای رابط کاربری. یعنی در Bottom Sheet استفاده از این layout باعث میشود تا باز و بسته شدن آن با یک حالت انیمیشن همراه باشد که نسبت به یک باز و بسته شدن عادی و ناگهانی، حس و تجربه بهتری را به کاربر منتقل میکند. CoordinatorLayout کاربردهای گسترده ای دارد که در مباحث آتی و در جای مناسب اشاره خواهد شد. به عنوان مثال در برخی از اپ ها با اسکرول به پایین، Toolbar حذف و با اسکرول به بالا، مجدد ظاهر میشود که پیاده سازی این عمل با CoordinatorLayout انجام میپذیرد.
ابتدا یک Bottom Sheet ساده تعریف کرده و در ادامه توضیحات لازم را قید میکنم:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:orientation="vertical" android:background="#009688" android:padding="8dp" app:layout_behavior="android.support.design.widget.BottomSheetBehavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="20sp" android:textStyle="bold" android:text="Bottom Sheet Title" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="16sp" android:text="Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et. Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et." /> </LinearLayout> </android.support.design.widget.CoordinatorLayout>
یک LinearLayout به layout اضافه کردم که حاوی دو TextView میباشد. در انتخاب نوع این Layout محدودیتی نیست و از سایر موارد مانند RelativeLayout، ConstraintLayout و… هم میتوان استفاده کرد. کاری که باید انجام دهیم، تعیین این LinearLayout به عنوان یک Bottom Sheet است که به اینصورت انجام شده:
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
با افزودن ویژگی فوق به یک layout، اندروید آنرا به عنوان یک Bottom Sheet درنظر میگیرد. مقدار این ویژگی را به صورت دیگری هم میتوانیم تعریف کنیم:
app:layout_behavior="@string/bottom_sheet_behavior"
کلید Ctrl را نگه داشته، روی مقدار فوق کلیک کنید. به فایل values.xml مربوط به کتابخانه design منتقل میشوید که در cache سیستم شما ذخیره شده است. به عنوان فوق برنامه، مسیر محل قرارگیری این فایل که در نوار آدرس اندروید استودیو نمایش داده میشود را در درایو مربوطه باز کنید. فولدرهای قبل آن را هم بررسی کنید. تمامی کتابخانه هایی که قبلا یکبار استفاده کردید در اینجا ذخیره شده و برای پروژه های بعدی استفاده میشود.
پروژه را اجرا میکنم:
یک نوار با رنگ پس زمینه و ارتفاع ۲۰۰dp که قبلا تعیین کردم در پایین صفحه ظاهر شده که محتوای آن شامل دو TextViewمیباشد. در حال حاضر این یک نوار ثابت است که با کشیدن به سمت پایین هیچ واکنشی صورت نمیپذیرد. خاصیت زیر را با یک مقدار دلخواه به Bottom Sheet (یعنی LinearLayout) اضافه میکنم:
app:behavior_peekHeight="60dp"
مجدد پروژه را اجرا میکنم:
با اضافه شدن خاصیت فوق، نوار ابتدا ارتفاعی برابر مقدار تعریف شده یعنی ۶۰dp دارد که با کشیدن نوار به سمت بالا، تا حداکثر ارتفاع خود یعنی ۲۰۰dp باز شده و مجددا با کشیدن آن به سمت پایین، به ارتفاع اولیه یعنی ۶۰dp برمیگردد.
واضح است اگر مقدار behavior_peekHeight را برابر با ۰ قرار بدهم، در حالت پیش فرض نوار کاملا مخفی است که در این پروژه قابل استفاده نیست. زیرا برای بالا آوردن نوار مخفی استفاده از جاوا لازم است که در این پروژه قصد کار با جاوا را نداریم.
در قسمت قبل با کشیدن نوار به سمت پایین، در ارتفاع اولیه یعنی ۶۰dp متوقف میشود. اما با استفاده از خاصیت behavior_hideable و مقدار true، نوار به طور کلی به پایین صفحه رفته و محو میشود:
app:behavior_hideable="true"
بدیهی است مقدار false برای خاصیت فوق، مانند قسمت قبل عمل کرده و نوار کاملا مخفی نمیشود.
سورس کامل activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:orientation="vertical" android:background="#009688" android:padding="8dp" app:behavior_peekHeight="60dp" app:behavior_hideable="true" app:layout_behavior="@string/bottom_sheet_behavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="20sp" android:textStyle="bold" android:text="Bottom Sheet Title" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="16sp" android:text="Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et. Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et." /> </LinearLayout> </android.support.design.widget.CoordinatorLayout>
در قسمت قبل، layout نوار Bottom Sheet از نوع LinearLayout بود. من در TextView دوم یک متن نسبتا طولانی استفاده کردم. اما در حال حاضر فقط قسمتی از متن و بطور کل محتوای نوار برای کاربر نمایش داده میشود و مابقی آن مخفی است. یعنی فقط به اندازه ۲۰۰dp را نشان میدهد و قابلیت اسکرول فعال نیست تا کاربر بتواند ادامه محتوا را نیز مشاهده کند. جهت رفع این مساله لازم است یک NestedScrollView را جایگزین LinearLayout کنیم:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="200dp" android:background="#009688" android:padding="8dp" app:behavior_peekHeight="60dp" app:behavior_hideable="true" app:layout_behavior="@string/bottom_sheet_behavior"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="20sp" android:textStyle="bold" android:text="Bottom Sheet Title" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:textSize="16sp" android:text="Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et. Lorem ipsum dolor sit amet, te sed vide delicata, per ex salutandi intellegat temporibus, ei insolens molestiae vis. Eum ei possim aperiam, fuisset suscipit vim ut. Voluptua repudiare gubergren id eum, nullam labores an nam. Sed et tota quaerendum, enim elit democritum quo et." /> </LinearLayout> </android.support.v4.widget.NestedScrollView> </android.support.design.widget.CoordinatorLayout>
(دقت کنید داخل NestedScrollView جهت چینش عناصر از یک LinearLayout استفاده شده)
حالا با اجرای مجدد پروژه امکان اسکرول محتوا نیز فراهم شده و بعد از آنکه نوار به سمت بالا کشیده شد و در حداکثر ارتفاع خود یعنی ۲۰۰dp قرار گرفت، با اسکرول مجدد به سمت بالا، ادامه محتوا نمایش داده میشود و به همین ترتیب برای اسکرول به سمت پایین.
در پروژه قبل با کلیّت این کامپوننت متریالی آشنا شدیم. در ادامه قصد دارم دو نوع Persistent و Modal را در دو پروژه جداگانه بررسی کنم. البته شاید نتوان از نظر تفاوت کاربرد این دو، یک مرز مشخص تعیین کرد اما بعد از مطالعه ادامه مبحث، تا حدود زیادی نوع کاربرد هریک از این دو مورد روشن میشود.
Persistent Bottom Sheet:
از این حالت عموما جهت نمایش محتوا استفاده میشود. مواردی مانند “جزئیات بیشتر”، “اطلاعات تکمیلی” و سایر موارد مشابه که بین محتوای اصلی موجود در صفحه و محتوای Bottom Sheet یک ارتباط وجود دارد و در واقع به نوعی این نوار نقش تکمیل کننده صفحه اصلی را دارد.
مجدد به مثال اولی که در ابتدای مبحث اشاره شد دقت کنید:
کاربر با انتخاب یک نقطه بر روی نقشه، ابتدا اطلاعات کلی از جمله نام مکان و مسافت را مشاهده میکند که در ادامه با کشیدن نوار به بالا یا انتخاب دکمه MORE INFO، توضیحات تکمیلی را نیز دریافت خواهد کرد. به عبارت دیگر، محتوای این نوار، اطلاعات داخلی برنامه محسوب میگردد و ارتباطی با خارج از آن ندارد.
یک پروژه با نام PersistentBottomSheet و یک Empty Activity ایجاد میکنم.
مانند پروژه قبل، اینجا هم لازم است کتابخانه design را اضافه کنم.
در این پروژه میخواهم با کلیک روی یک دکمه، یک Bottom Sheet باز و بسته شود. بنابراین به دلیل آنچه در پروژه قبل ذکر شد، ابتدا layout ریشه را به CoordinatorLayout تغییر داده، سپس یک Button به آن اضافه میکنم:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_expand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Expand" /> </RelativeLayout> </android.support.design.widget.CoordinatorLayout>
CoordinatorLayout امکان مدیریت نحوه چینش عناصر را ندارد بنابراین دکمه را درون یک RelativeLayout قرار دادم. در مرحله بعد یک Bottom Sheet به layout اضافه میکنم:
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/bottom_sheet" android:background="#94aab4" android:padding="8dp" app:behavior_hideable="true" app:behavior_peekHeight="60dp" app:layout_behavior="@string/bottom_sheet_behavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" android:text="Location: Iran" android:paddingBottom="10dp" android:paddingTop="10dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#452f2f" android:textSize="16sp" android:text="Iran is an Islamic republic on the Persian Gulf with historical sites dating to the Persian Empire. Extensive marble ruins mark Persepolis, the empire’s capital founded by Darius I in the 6th century B.C. The modern capital, Tehran, is home to opulent Golestan Palace, seat of the Qajar Dynasty (1794–۱۹۲۵), plus modern landmarks such as the 435m-high Milad Tower."/> </LinearLayout>
در اینجا از NestedScrollView استفاده نکردم. ارتفاع هم wrap_content تعیین کردم بنابراین نوار به اندازه محتوای درون آن باز خواهد شد.
بهتر است برای Bottom Sheet یک layout جداگانه ساخته و آنرا درون layout اکتیویتی اصلی include کنم تا نظم بیشتری به کار داده باشم. یک Layout جدید با نام 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:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/bottom_sheet" android:background="#94aab4" android:padding="8dp" app:behavior_hideable="true" app:behavior_peekHeight="60dp" app:layout_behavior="@string/bottom_sheet_behavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" android:text="Location: Iran" android:paddingBottom="10dp" android:paddingTop="10dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#452f2f" android:textSize="16sp" android:text="Iran is an Islamic republic on the Persian Gulf with historical sites dating to the Persian Empire. Extensive marble ruins mark Persepolis, the empire’s capital founded by Darius I in the 6th century B.C. The modern capital, Tehran, is home to opulent Golestan Palace, seat of the Qajar Dynasty (1794–۱۹۲۵), plus modern landmarks such as the 435m-high Milad Tower."/> </LinearLayout>
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_expand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Expand" /> </RelativeLayout> <include layout="@layout/bottom_sheet" /> </android.support.design.widget.CoordinatorLayout>
در حال حاضر با اجرای پروژه، ۶۰dp از ابتدای نوار نمایش داده میشود که با کشیدن به سمت بالا، نوار کامل باز خواهد شد. اما قصد دارم این عمل باز شدن توسط دکمه صورت پذیرد.
اکتیویتی را به اینصورت تکمیل میکنم:
package ir.android_studio.persistentbottomsheet; import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity { Button btnSheet; LinearLayout sheetLayout; BottomSheetBehavior bottomSheet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnSheet = findViewById(R.id.btn_expand); sheetLayout = findViewById(R.id.bottom_sheet); bottomSheet = BottomSheetBehavior.from(sheetLayout); btnSheet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); } }); } }
ابتدا Button و LinearLayout مربوط به BottomSheet را توسط id ای که قبلا به آنها اختصاص دادم تعریف کردم. سپس یک نمونه از متد BottomSheetBehavior با نام دلخواه bottomSheet ساختم که توسط متد .from() لایه مربوطه را به آن معرفی کرده ام.
View sheetLayout;
در ادامه برای دکمه یک Listener ایجاد کردم. از متد setState() جهت تعیین حالت Bottom Sheet استفاده میشود. من حالت STATE_EXPANDED را انتخاب کردم، یعنی با لمس دکمه، نوار در وضعیت Expanded (باز شده) قرار میگیرد.
پروژه را اجرا و تست میکنم:
عملیات به درستی انجام میشود و نوار باز شد. در قدم بعد میخواهم با لمس دوبارهی دکمه، نوار مجددا بسته شود:
btnSheet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (bottomSheet.getState() == BottomSheetBehavior.STATE_COLLAPSED) { bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); } else if (bottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) { bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); } } });
با استفاده از if else و متد getState() شرطی تعیین کردم تا اگر نوار در وضعیت Collapsed بود، با لمس دکمه به وضعیت Expanded تغییر کند و بلعکس.
در هریک از دو نقش فوق، دکمه عبارت Expand را نشان میدهد که منطقی نیست. هنگامی که نوار در وضعیت Expand قرار دارد، نقش دکمه Collapse کردن آن است بنابراین عنوان دکمه نیز باید تغییر کند که با استفاده از setText() درون شرط این اصلاح را صورت میدهم:
btnSheet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (bottomSheet.getState() == BottomSheetBehavior.STATE_COLLAPSED) { bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); btnSheet.setText("Collapse"); } else if (bottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) { bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); btnSheet.setText("Expand"); } } });
با اجرای مجدد پروژه، نوار در هر وضعیتی که باشد، دکمه عبارت متضاد آنرا نشان میدهد.
هنوز یک ایراد دیگر داریم. در حال حاضر اگر وضعیت نوار را فقط با دکمه تغییر دهید همه چیز به خوبی انجام شده و با تغییر حالت نوار، text دکمه هم تغییر میکند. اما در صورتی که کاربر نوار را به صورت عادی به طرف بالا یا پایین بکشد، تغییری در text انجام نمیشود که یک خطای منطقی است. یعنی اگر دکمه عبارت Expand را نشان میدهد و کاربر نوار را بدون استفاده از دکمه Expand کند (بالا ببرد)، باز هم دکمه کلمه Expand را نشان میدهد در صورتی که باید با کلمه Collapse جایگزین شود.
در اینجا متدی داریم با نام setBottomSheetCallback که امکان مدیریت Bottom Sheet را فراهم میکند:
bottomSheet.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } });
این متد دو تابع onStateChanged و onSlide دارد که برای رفع مشکل ایجاد شده از مورد اول استفاده میکنیم:
public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { btnSheet.setText("Collapse"); } else if (newState == BottomSheetBehavior.STATE_COLLAPSED) { btnSheet.setText("Expand"); } }
در اینجا با استفاده از newState() یک شرط تعیین شده. به اینصورت که اگر وضعیت جدید روی Expanded بود، دکمه کلمه Collapse را نشان دهد و بلعکس. حالا با اجرای مجدد پروژه همه چیز به درستی کار میکند.
با تعیین این شرط، عملا setText هایی که در Listener دکمه تعریف کرده بودم کارایی ندارد و آنها را حذف میکنم (در سورس پروژه comment شده است).
MainActivity.java:
package ir.android_studio.persistentbottomsheet; import android.support.annotation.NonNull; import android.support.design.widget.BottomSheetBehavior; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; public class MainActivity extends AppCompatActivity { Button btnSheet; LinearLayout sheetLayout; BottomSheetBehavior bottomSheet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnSheet = findViewById(R.id.btn_expand); sheetLayout = findViewById(R.id.bottom_sheet); bottomSheet = BottomSheetBehavior.from(sheetLayout); btnSheet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (bottomSheet.getState() == BottomSheetBehavior.STATE_COLLAPSED) { bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); //btnSheet.setText("Collapse"); } else if (bottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) { bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); //btnSheet.setText("Expand"); } } }); bottomSheet.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { btnSheet.setText("Collapse"); } else if (newState == BottomSheetBehavior.STATE_COLLAPSED) { btnSheet.setText("Expand"); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } }); } }
دو خاصیت behavior_peekHeight و behavior_hideable را به جای xml میتوان در جاوا نیز تعریف و کنترل کرد (در سورس کامنت شده):
bottomSheet.setPeekHeight(0); bottomSheet.setHideable(true);
Modal Bottom Sheet:
در این نوع، در حقیقت ما یک Dialog (مشابه AlertDialog) را به عنوان یک Bottom Sheet نمایش میدهیم. مانند مثال دوم ابتدای مبحث. یعنی Google Drive:
از Modal عموما جهت نمایش گزینه هایی مانند Share، Copy، Upload و سایر اکشن های مشابه استفاده میشود.
یک پروژه جدید با نام ModalBottomSheet و یک Empty Activity ایجاد میکنم.
مشابه موارد قبل ابتدا کتابخانه Support Design را به پروژه اضافه میکنم.
activity_main.xml را مشابه پروژه قبل میسازم با این تفاوت که layout ای که include شده بود حذف میشود:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_expand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Expand" /> </RelativeLayout> </android.support.design.widget.CoordinatorLayout>
برای نمایش Bottom Sheet در قالب یک Dialog، یک فرگمنت میسازم. بعد از ساخته شدن فرگمنت، باید ارث بری آن را از Fragment به BottomSheetDialogFragment تغییر دهم:
BottomSheetFragment.java:
package ir.android_studio.modalbottomsheet; import android.os.Bundle; import android.support.design.widget.BottomSheetDialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class BottomSheetFragment extends BottomSheetDialogFragment { public BottomSheetFragment() { // Required empty public constructor } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_bottom_sheet, container, false); } }
حذف import android.support.v4.app.Fragment از کلاس فرگمنت فراموش نشود.
حالا باید layout این فرگمنت را تکمیل کنم. همان محتوایی که برای bottom_sheet.xml پروژه قبل ساخته بودم را در اینجا استفاده میکنم:
fragment_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:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/bottom_sheet" android:background="#94aab4" android:padding="8dp" app:behavior_hideable="true" app:behavior_peekHeight="60dp" app:layout_behavior="@string/bottom_sheet_behavior"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="20sp" android:text="Location: Iran" android:paddingBottom="10dp" android:paddingTop="10dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#452f2f" android:textSize="16sp" android:text="Iran is an Islamic republic on the Persian Gulf with historical sites dating to the Persian Empire. Extensive marble ruins mark Persepolis, the empire’s capital founded by Darius I in the 6th century B.C. The modern capital, Tehran, is home to opulent Golestan Palace, seat of the Qajar Dynasty (1794–۱۹۲۵), plus modern landmarks such as the 435m-high Milad Tower."/> </LinearLayout>
در مرحله نهایی، یک متد Listener برای دکمه Expand مینویسم و داخل آنرا به صورت زیر تکمیل میکنم:
package ir.android_studio.modalbottomsheet; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { Button btnSheet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnSheet = findViewById(R.id.btn_expand); btnSheet.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { BottomSheetFragment bottomFragment = new BottomSheetFragment(); bottomFragment.show(getSupportFragmentManager(), bottomFragment.getTag()); } }); } }
ابتدا از فرگمنت یک نمونه با نام bottomFragment ساخته شده. سپس توسط متد show() نمایش داده میشود. این متد دو پارامتر ورودی میگیرد که اولی FragmentManager و دومی Tag فرگمنت مربوطه است.
پروژه را اجرا میکنم:
مانند AlertDialog، این نوار نیز دارای یک elevation (ارتفاع) زیاد است و با دکمه back دیوایس و یا کشیدن نوار به سمت پایین حذف میشود.
منابع تکمیلی:
https://developer.android.com/reference/android/support/design/widget/BottomSheetBehavior
https://developer.android.com/reference/android/support/design/widget/BottomSheetDialog
https://developer.android.com/reference/android/support/design/widget/BottomSheetDialogFragment
تعداد صفحات : ۲۵
حجم : ۲/۳ مگابایت
قیمت : رایگان
دانلود رایگان با حجم ۲/۳ مگابایت لینک کمکی
ممنونم با آرزوی سلامتی ثروت زیاد
سلام تو مورد آخری اگر تو صفحه ای که به عنوان شیت میاد باتن داشته باشیم نتیجه و کد های مربوط به اون باتن رو کجا باید بنویسم؟
داخل BottomSheetFragment.java
سلام و خسته نباشید
من می خواستم اسم این که تو صفحه اول پیام رسان است که اگر به چپ یا راست پیمایش کنیم مثل اسکرول به چپ و راست است اسم اون چیه
Slider
Carousel
هرچند سوالتون به این مبحث مربوط نبود
ازتون یک دنیا ممنون خیلی عالی آموزش دادین
مرسی دادا