ساخت منوی کشویی در اندروید — آموزش ساخت گام به گام و تصویری
تیم متریال دیزاین گوگل کارکرد منوی کشویی در اندروید را چنین تعریف کرده است: منوی کشویی به منویی گفته میشود که از سمت چپ وارد صفحه میشود و شامل مقاصد ناوبری برای اپلیکیشن است. همین تعریف شاید به تنهایی بتواند توضیح دهد که چرا یک توسعه دهنده اندروید باید با ساخت منوی کشویی در اندروید آشنایی کافی را داشته باشد. نمونهای از اپلیکیشنهای محبوب اندروید که منوی کشویی را پیادهسازی کرده است، اپلیکیشن Inbox گوگل محسوب میشود که از منوی کشویی برای حرکت به بخشهای مختلف اپلیکیشن استفاده میکند. در تصویر زیر اپلیکیشن اینباکس و منوی کشویی آن را مشاهده میکنید.
کاربر میتواند منوی کشویی را با سوایپ کردن از سمت چپ به داخل صفحه بکشد. همچنین امکان پیدا کردن آن از اکتیویتی Home یعنی بالاترین سطح اپلیکیشن با ضربه زدن روی آیکون منوی همبرگری در اکشن بار وجود دارد. در این مطلب به بحث ساخت منوی کشویی در اندروید میپردازیم و یک پروژه ساخت منوی کشویی در اندروید را نیز به صورت کامل مرور میکنیم
طراحی و ساخت منوی کشویی در اندروید
توجه کنید که اگر مقاصد زیادی (بیش از شش عدد) در اپلیکیشن خود دارید، بهتر است از «منوی کشویی» (Navigation Drawer) استفاده کنید. در این مطلب با روش نمایش آیتمهای ناوبری درون یک منوی کشویی در اندروید آشنا خواهیم شد. به این ترتیب با شیوه استفاده از API-های DrawerLayout و NavigationView برای اجرای این وظیفه آشنا میشویم. همچنین با روش استفاده از قالبهای آماده اندروید استودیو برای ساخت سریع پروژههای دارای منوی کشویی آشنا خواهیم شد.
یک پروژه نمونه به زبان کاتلین در این راهنما ارائه شده است که میتوانید در صفحه گیتهاب ارائه شده آن را به طور کامل مشاهده کنید.
پیشنیازها
برای این که بتوانید این راهنما را به طور عملی دنبال کنید به پیشنیازهای زیر نیاز خواهید داشت:
- اندروید استودیو 3.0 یا بالاتر.
- پلاگین کاتلین 1.1.51 یا بالاتر.
ساخت پروژه اندروید استودیو
اندروید استودیو را باز کرده و پروژه جدیدی به نام NavigationDrawerDemo در آن ایجاد کنید که شامل یک اکتیویتی خالی به نام MainActivity باشد. مطمئن شوید که تیک گزینه Include Kotlin support را زدهاید.
افزودن DrawerLayout و NavigationView
برای این که بتوانید از DrawerLayout و NavigationView در یک پروژه ساخت منوی کشویی در اندروید استفاده کنید، باید design support و همچنین Android support را ایمپورت کنید. بنابراین باید موارد زیر را به فایل ماژول اضافه کنید تا گزینههای لازم ایمپورت شوند:
1dependencies {
2 implementation 'com.android.support:design:27.0.2'
3 implementation 'com.android.support:support-v4:27.0.2'
4}
همچنین باید هر دو ویجت DrawerLayout و NavigationView را در فایل res/layout/activlty_main.xml بگنجانید:
1<?xml version="1.0" encoding="utf-8"?>
2<android.support.v4.widget.DrawerLayout
3 xmlns:android="https://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:id="@+id/drawer_layout"
7 android:layout_width="match_parent"
8 android:layout_height="match_parent"
9 tools:openDrawer="start">
10
11 <include
12 layout="@layout/app_bar_main"
13 android:layout_width="match_parent"
14 android:layout_height="match_parent" />
15
16 <android.support.design.widget.NavigationView
17 android:id="@+id/nav_view"
18 android:layout_width="wrap_content"
19 android:layout_height="match_parent"
20 android:layout_gravity="start"
21 app:headerLayout="@layout/nav_header_main"
22 app:menu="@menu/activity_main_drawer" />
23
24</android.support.v4.widget.DrawerLayout>
در کد فوق یک ویجت DrawerLayout با شناسه drawer_layout ایجاد کردهایم. مشخصه tools:openDrawer برای نمایش منوی کشویی در زمانی که لیآوت XML در نمای طراحی اندروید استودیو قرار دارد استفاده میشود. مستندات رسمی در مورد DrawerLayout چنین بیان میکنند:
DrawerLayout به عنوان یک کانتینر سطح بالا برای محتوای پنجره عمل میکند که امکان تعامل نماهای drawer و کشیده شدن از یکی از دو سمت عمودی پنجره به داخل را فراهم میسازند.
پس از آن که ویجت DrawerLayout را اضافه کردهاید باید لیآوت فرزند را که به layout/app_bar_main@ اشاره میکند را نیز بگنجانید. در کد زیر فایل منبع app_bar_main.xml را میبینید که یک CoordinatorLayout، یک AppBarLayout و یک Toolbar دارد:
1<?xml version="1.0" encoding="utf-8"?>
2<android.support.design.widget.CoordinatorLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 tools:context=".MainActivity">
9
10 <android.support.design.widget.AppBarLayout
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:fitsSystemWindows="true"
14 android:theme="@style/AppTheme.AppBarOverlay">
15
16 <android.support.v7.widget.Toolbar
17 android:id="@+id/toolbar_main"
18 android:layout_width="match_parent"
19 android:layout_height="?attr/actionBarSize"
20 android:background="?attr/colorPrimary"
21 app:layout_scrollFlags="scroll|enterAlways"
22 app:popupTheme="@style/AppTheme.PopupOverlay" />
23
24 </android.support.design.widget.AppBarLayout>
25</android.support.design.widget.CoordinatorLayout>
در نهایت ویجت NavigationView را ایجاد میکنیم. مستندات رسمی در مورد NavigationView چنین بیان میکنند:
NavigationView نماینده یک منوی ناوبری استاندارد برای اپلیکیشن است. محتوای این منو میتواند با استفاده از فایل ریسورس مقداردهی شود.
در ویجت XML به نام NavigationView میبینیم که یک خصوصیت با مقدار start اضافه شده است. این مقدار برای قراردهی منوی کشویی استفاده میشود. ما میتوانیم کاری کنیم که منوی کشویی از سمت راست یا چپ به داخل بیاید که به ترتیب باید از مقادیر start یا end استفاده شود. در این مورد منوی کشویی از سمت چپ وارد صفحه خواهد شد.
همچنین از خصوصیت app:headerLayout استفاده کردیم که به layout/nav_header_main@ اشاره میکند. به این ترتیب یک View به عنوان هدر به منوی کشویی در اندروید اضافه میشود. فایل منبع لیآوت آن به نام چنین است:
1<?xml version="1.0" encoding="utf-8"?>
2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:id="@+id/nav_header"
4 android:layout_width="match_parent"
5 android:layout_height="160dp"
6 android:background="@color/colorAccent"
7 android:clickable="true"
8 android:focusable="true"
9 android:foreground="?attr/selectableItemBackgroundBorderless"
10 android:gravity="bottom"
11 android:orientation="vertical"
12 android:padding="16dp"
13 android:theme="@style/ThemeOverlay.AppCompat.Dark">
14
15 <ImageView
16 android:id="@+id/nav_header_imageView"
17 android:layout_width="64dp"
18 android:layout_height="64dp"
19 android:src="@mipmap/ic_launcher" />
20
21 <TextView
22 android:id="@+id/nav_header_textView"
23 android:layout_width="match_parent"
24 android:layout_height="wrap_content"
25 android:paddingTop="16dp"
26 android:text="Chike Mgbemena"
27 android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
28</LinearLayout>
این لیآوت صرفاً شامل یک LinearLayout، یک ImageView و یک TextView است.
برای قرار دادن آیتمهای منو در پروژه ساخت منوی کشویی در اندروید میتوانیم از خصوصیت app:menu با مقداری که به فایل منبع منو اشاره میکند بهره بگیریم:
1<android.support.design.widget.NavigationView
2 app:menu="@menu/activity_main_drawer" />
فایل منبع منوی ما به نام res/menu/activity_main_drawer.xml به صورت زیر است:
1<?xml version="1.0" encoding="utf-8"?>
2<menu xmlns:android="http://schemas.android.com/apk/res/android">
3 <group>
4 <item android:id="@+id/nav_item_one"
5 android:icon="@drawable/ic_drafts_black_24dp"
6 android:title="Item 1" />
7 <item android:id="@+id/nav_item_two"
8 android:icon="@drawable/ic_drafts_black_24dp"
9 android:title="Item 2" />
10 <item android:id="@+id/nav_item_three"
11 android:icon="@drawable/ic_drafts_black_24dp"
12 android:title="Item 3" />
13 </group>
14
15 <group android:id="@+id/group_menu">
16 <item android:id="@+id/nav_item_four"
17 android:title="Item 4" />
18 <item android:id="@+id/nav_item_five"
19 android:title="Item 5" />
20 </group>
21
22 <item android:title="Title 1">
23 <menu>
24 <item android:id="@+id/nav_item_six"
25 android:icon="@drawable/ic_drafts_black_24dp"
26 android:title="Item 6" />
27 <item android:id="@+id/nav_item_seven"
28 android:icon="@drawable/ic_drafts_black_24dp"
29 android:title="Item 7" />
30 </menu>
31 </item>
32</menu>
در این فایل یک Menu با استفاده از تگ <menu> تعریف کردهایم که به عنوان یک کانتینر برای آیتمهای منو عمل میکند. یک <item> موجب میشود یک MenuItem ایجاد شود که نماینده یک آیتم منفرد در منو است.
در ادامه گروه منوی اول را با استفاده از <group> تعریف کردهایم. تگ <group> به عنوان یک کانتینر برای عناصر <item> عمل میکند که در این مورد آیتمهای منو هستند. هر یک از عناصر <item> یک شناسه و یک عنوان دارند. توجه کنید که یک خط افقی در انتهای هر <group> کشیده میشود که در منوی کشویی نمایان خواهد بود.
هر <item> میتواند شامل یک عنصر <menu> تودرتو باشد که یک زیرمنو ایجاد میکند و ما این کار را در مورد آخرین <item> انجام دادیم. توجه کنید که این <item> آخر دارای مشخصه عنوان است.
همچنین توجه کنید که وقتی آیتمهای لیست ناوبری از یک منبع منو نمایش مییابند، میتوانیم از یک ListView نیز استفاده کنیم. اما با پیکربندی منوی کشویی به وسیله فایل منبع یک استایلبندی طراحی متریال روی منوی کشویی نیز به دست میآوریم. اگر از یک ListView استفاده میکنیم، باید این لیست را خودمان نگهداری کنیم و آن را طوری استایلبندی نماییم که با مشخصههای طراحی متریال برای منوی کشویی سازگار باشد.
مقداردهی اجزا
در گام بعدی ساخت منوی کشویی در اندروید قصد داریم وهلههای DrawerLayout و ActionBarDrawerToggle را مقداردهی کنیم. این مقداردهی درون متد ()onCreate در فایل MainActivity.kt انجام مییابد.
1import android.content.res.Configuration
2import android.os.Bundle
3import android.support.v4.widget.DrawerLayout
4import android.support.v7.app.ActionBarDrawerToggle
5import android.support.v7.app.AppCompatActivity
6import android.support.v7.widget.Toolbar
7import android.view.MenuItem
8
9class MainActivity : AppCompatActivity() {
10
11 private lateinit var drawer: DrawerLayout
12 private lateinit var toggle: ActionBarDrawerToggle
13
14 override fun onCreate(savedInstanceState: Bundle?) {
15 super.onCreate(savedInstanceState)
16 setContentView(R.layout.activity_main)
17
18 val toolbar: Toolbar = findViewById(R.id.toolbar_main)
19 setSupportActionBar(toolbar)
20
21 drawer = findViewById(R.id.drawer_layout)
22
23 toggle = ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
24 drawer.addDrawerListener(toggle)
25 supportActionBar?.setDisplayHomeAsUpEnabled(true)
26 supportActionBar?.setHomeButtonEnabled(true)
27 }
28
29 // ...
30}
متد ActionBarDrawerToggle آیکون اپلیکیشن را تنظیم میکند که در سمت چپ اکشن بار یا نوار ابزار قرار دارد و به منظور باز و بسته کردن منوی کشویی استفاده میشود. برای این که بتوانیم وهلهای از ایجاد کنیم باید پارامترهای زیر را عرضه کنیم:
- یک کانتکست والد – برای نمونه در یک اکتیویتی باید از this استفاده کنیم، در حالی که در یک فرگمان باید ()getActivity را فراخوانی کنیم.
- یک وهله از ویجت DrawerLayout که به ActionBar اکتیویتی پیوند مییابد.
- آیکون که در روی آیکون اپلیکیشن قرار میگیرد تا نشاندهنده دوحالتی بودن آن باشد.
- منبع رشتهها برای عملیات باز و بسته کردن جهت کاربردپذیری
ما متد ()addDrawerListener را روی یک DrawerLayout احضارکردهایم و از این رو ActionBarDrawerToggle به یک DrawerLayout وصل شده است. توجه کنید که ما آیکون اپلیکیشن را از طریق ()setHomeButtonEnabled نیز فعال کردهایم و آن را برای ناوبری به سمت بالا از طریق با استفاده از ()setDisplayHomeAsUpEnable فعال نمودهایم.
سپس متدهای کالبک اکتیویتی ()onPostCreate() onConfigurationChanged و ()onOptionsItemSelected را در زمان باز و بسته شدن ارسال میکنیم:
1class MainActivity : AppCompatActivity() {
2
3 // ...
4
5 override fun onPostCreate(savedInstanceState: Bundle?) {
6 super.onPostCreate(savedInstanceState)
7 toggle.syncState()
8 }
9
10 override fun onConfigurationChanged(newConfig: Configuration?) {
11 super.onConfigurationChanged(newConfig)
12 toggle.onConfigurationChanged(newConfig)
13 }
14
15 override fun onOptionsItemSelected(item: MenuItem?): Boolean {
16 if (toggle.onOptionsItemSelected(item)) {
17 return true
18 }
19 return super.onOptionsItemSelected(item)
20 }
21}
در کد فوق ()syncState کارهای زیر را انجام میدهد:
حالت نشانگر منوی کشویی که با DrawerLayout پیوند یافته را همگامسازی میکند. این متد باید از متد onPostCreate اکتیویتی احضار شود تا حالت وهله DrawerLayout که بازیابی شده، همگامسازی شود و هر دفعه دیگر که حالت طوری تغییر یافت که ActionBarDrawerToggle مطلع نشد نیز باید از آن استفاده کرد.
تست کردن اپلیکیشن
در این مرحله میتوانیم اپلیکیشن خود را اجرا کنیم:
چنان که میبینید، با اجرا کردن اپلیکیشن، آیکون منوی کشویی همبرگری در اکشن بار ظاهر میشود. اگر روی این آیکون بزنید منو باز خواهد شد. همچنان با کیک کردن روی آیتمهای منو هیچ اتفاقی نمیافتد. این موضوع را در بخش بعدی حل میکنیم.
مدیریت رویدادهای کلیک
اکنون به بررسی شیوه مدیریت رویدادهای کلیک آیتمها در ساخت منوی کشویی در اندروید میپردازیم. توجه کنید که کلیک کردن روی هر آیتم شما را به یک اکتیویتی یا فرگمان جدید میبرد و به همین دلیل است که آن را منوی ناوبری نیز میخوانند.
ابتدا اکتیویتی شما باید NavigationView.OnNavigationItemSelectedListener را پیادهسازی کند:
1class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
2 // ...
3}
با پیادهسازی این کنتراکت یا اینترفیس، اکنون باید تنها متد ()onNavigationItemSelected را اُورراید کنیم:
1class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
2
3 // ...
4
5 override fun onNavigationItemSelected(item: MenuItem): Boolean {
6
7 when (item.itemId) {
8 R.id.nav_item_one -> Toast.makeText(this, "Clicked item one", Toast.LENGTH_SHORT).show()
9 R.id.nav_item_two -> Toast.makeText(this, "Clicked item two", Toast.LENGTH_SHORT).show()
10 R.id.nav_item_three -> Toast.makeText(this, "Clicked item three", Toast.LENGTH_SHORT).show()
11 R.id.nav_item_four -> Toast.makeText(this, "Clicked item four", Toast.LENGTH_SHORT).show()
12 }
13 return true
14 }
15}
این متد زمانی فراخوانی میشود که یک آیتم در منوی کشویی انتخاب شده باشد. ما از عبارت when برای اجرای اکشنهای مختلف روی آیتم منو در زمان کلیک شدن استفاده میکنیم. شناسههای آیتم منو به عنوان محتوای عبارت when استفاده میشوند.
در ادامه باید NavigationView را مقداردهی کرده و شنونده را درون متد ()onCreate اکتیویتی تنظیم کنیم:
1class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
2 // ...
3 override fun onCreate(savedInstanceState: Bundle?) {
4 // ...
5 val navigationView: NavigationView = findViewById(R.id.nav_view)
6 navigationView.setNavigationItemSelectedListener(this)
7 // ...
8 }
9// ...
اینک اپلیکیشن را دوباره اجرا میکنیم:
زمانی که روی برخی آیتمها کلیک میکنیم، یک پیام توست نمایش مییابد که دقیقاً مطابق انتظار ما است، اما به خاطر بسپارید که کلیک کردن یک آیتم باید از سوی کاربر باید منتهی به ساخت یک اکتیویتی یا فرگمان جدید شود.
احتمالاً متوجه شدهاید که وقتی روی یک آیتم کلیک میکنید، منوی کشویی همچنان باز میماند. بهتر است این منو در زمان کلیک شدن یک آیتم به طور خودکار بسته شود. روش انجام این کار چنین است:
1override fun onNavigationItemSelected(item: MenuItem): Boolean {
2
3 when (item.itemId) {
4 R.id.nav_item_one -> Toast.makeText(this, "Clicked item one", Toast.LENGTH_SHORT).show()
5 R.id.nav_item_two -> Toast.makeText(this, "Clicked item two", Toast.LENGTH_SHORT).show()
6 R.id.nav_item_three -> Toast.makeText(this, "Clicked item three", Toast.LENGTH_SHORT).show()
7 R.id.nav_item_four -> Toast.makeText(this, "Clicked item four", Toast.LENGTH_SHORT).show()
8 }
9 drawer.closeDrawer(GravityCompat.START)
10 return true
11}
برای بستن منو پس از کلیک شدن آیتم کافی است متد ()closeDrawer را روی یک وهله از DrawerLayout فرا بخوانید و GravityCompat.START را به متد ارسال کنید. اینک اگر اپلیکیشن را مجدداً اجرا کنید نتیجه را میبینید.
مدیریت دکمه بازگشت
زمانی که منوی کشویی در اندروید باز میشود از نظر تجربه کاربری بهتر است که اکتیویتی اصلی در زمان فشرده شدن دکمه بازگشت بسته نشود. اپلیکیشنهای محبوب مانند اینباکس گوگل چنین عمل میکنند.
بنابراین زمانی که منوی کشویی باز است اگر دکمه بازگشت گوشی زده شود تنها منوی کشویی بسته میشود و اکتیویتی اصلی باز میماند. سپس اگر کاربر دوباره دکمه بازگشت را بزنید این بار اکتیویتی اصلی باید بسته شود. روش انجام این کار چنین است:
1override fun onBackPressed() {
2 if (drawer.isDrawerOpen(GravityCompat.START)) {
3 drawer.closeDrawer(GravityCompat.START)
4 } else {
5 super.onBackPressed()
6 }
7}
پروژه را دوباره اجرا کرده و این موضوع را تست کنید.
استفاده از قالبهای اندروید استودیو
اکنون که با API-های منوی کشویی آشنا شدید، یک راه میانبر نشان میدهیم که دفعه بعد منوی کشویی را سریعتر بساید. به این منظور میتوانید صرفاً از یک قالب به جای ساخت اکتیویتی منوی کشویی از صفر استفاده کند.
اندروید استودیو قالبهای مختلفی ارائه کرده است که از بهترین رویههای طراحی و توسعه برخوردار هستند. این فالیهای کد موجود به ساخت سریعتر پروژهها کمک میکنند. یکی از این قالبها که میتوانید استفاده کنید ساخت اکتیویتی منوی کشویی است. به این منظور در اندروید استودیو یک پروژه جدید ایجاد کنید.
نام اپلیکیشن را وارد کرده و روی Next کلیک کنید.
مقادیر پیشفرض موجود در دیالوگ Target Android Devices را دستکاری نکنید و دوباره روی Next کلیک کنید.
در دیالوگ Add an Activity to Mobile به سمت پایین اسکرول کرده و گزینه Drawer Activity. را انتخاب کنید. دوباره Next را بزنید.
در دیالوگ آخر میتوانید نام اکتیویتی، نام لیآوت یا عنوان آن را در صورت تمایل عوض کنید. در نهایت روی دکمه Finish کلیک کنید تا همه پیکربندیها تأیید شوند. اینک اندروید استودیو به شما کمک میکند تا یک اکتیویتی منوی کشویی ایجاد کنید.
البته ما توصیه میکنیم که کد این اکتیویتی را به طور کامل بررسی کنید. شما میتوانید این قالب را به یک پروژه موجود اندروید استودیو نیز اضافه کتید. کافی است به منوی File > New > Activity > Navigation Drawer Activity بروید.
قالبهایی که همراه اندروید استودیو میآیند، برای لیآوتهای ساده و ساخت اپلیکیشنهای ابتدایی مناسب هستند، اما اگر میخواهید اپلیکیشن خود را بیشتر توسعه دهید باید کارهای بیشتری انجام دهید. این قالبها برای توسعهدهندگان با تجربه مزیت بزرگی محسوب میشوند و در زمان آنها صرفهجویی میکند. به این ترتیب توسعهدهندگان میتوانند زحمت ساخت یک اپلیکیشن از صفر را کاهش داده و روی جوانبی از اپلیکیشن تمرکز کنند که منحصر به فرد هستند و قابلیت سفارشیسازی را دارند.
سخن پایانی
در این راهنما با شیوه ساخت منوی کشویی در اندروید با استفاده از API-های DrawerLayout و NavigationView از صفر آشنا شدید. همچنین روش ساخت سریع و آسا این منوها را با استفاده از قالبهای اندروید استودیو آموختیم.
ممنون از مقاله تون مشکل قبلی من با حذف supportsRtl حل شد
برای من از سمت راست باز شد
وقتی هم start رو به end تغییر دادم از چپ اومد
ولی اون دکمه ای که با یک کلیک منو رو باز میکرد وقتی روش کلیک میکردم کرش میکرد