猛禽洛的程式筆記庫

[Android] 側邊欄 DrawerLayout 使用教學

在很多APP中可以看到如下圖從螢幕側邊拉出抽屜的畫面

此功能是由DrawerLayout為基底,加上NavigationView組合而成。

下面介紹如何實作這項功能:

這次是要做出如圖的功能↓

1.布局Layout:由DrawerLayout為基底,加上自己的布局與NavigationView

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:background="?attr/colorPrimary"
            android:minHeight="?attr/actionBarSize"
            android:paddingTop="20dp"
            android:theme="?attr/actionBarTheme"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/drawer_header"
        app:insetForeground="@android:color/transparent"
        app:menu="@menu/home_drawer">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="bottom"
            android:orientation="vertical"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingBottom="10dp">

            <Button
                android:id="@+id/btn_logout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="登出" />
        </LinearLayout>
    </com.google.android.material.navigation.NavigationView>

</androidx.drawerlayout.widget.DrawerLayout>

其中,DrawerLayout底下包了兩個View,ConstraintLayout是我們的主畫面區域,NavigationView是側邊欄畫面區域。

注意:需要有個Toolbar,才能將側邊欄的開關綁定上去,不然就只能用手勢拖拉了! (或是自己做)

 

NavigationView的屬性中:

app:headerLayout=”@layout/drawer_header” 是Herder部分的畫面(用不到就非必要)

app:menu=”@menu/home_drawer” 是按鈕清單的畫面(用不到就非必要)

以上兩個畫面下面會解釋。

NavigationView中包的LinearLayout這裡是作為Footer來使用,放置登出功能,或是版本號之類..

 

在drawer_header.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:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageButton
        android:id="@+id/ibtn_DrawerHeader_close"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="48dp"
        android:adjustViewBounds="true"
        android:background="?android:attr/selectableItemBackgroundBorderless"
        android:padding="5dp"
        android:scaleType="fitCenter"
        android:src="@android:drawable/ic_menu_close_clear_cancel"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:text="item1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/ibtn_DrawerHeader_close" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:text="item2"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:text="item3"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button2" />

</androidx.constraintlayout.widget.ConstraintLayout>

建立了一個ImageButton與三個Button放在Header中。

 

在home_drawer.xml Menu佈局中:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/menu1"
        android:title="menu1" />
    <item
        android:id="@+id/menu2"
        android:title="menu2" />

</menu>

建立了兩個item供點選。

 

到這邊為止,Layout的設計準備就完成了。

2.程式碼實作:

class HomeActivity : AppCompatActivity() {

    private lateinit var mNavigationView: NavigationView
    private lateinit var mToolbar: Toolbar
    private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
    private lateinit var mDrawerLayout: DrawerLayout
    private lateinit var btnLogout: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        //綁定每項元件
        mDrawerLayout = findViewById(R.id.drawerLayout)
        mToolbar = findViewById(R.id.toolbar)
        mNavigationView = findViewById(R.id.navigationView)
        btnLogout = findViewById(R.id.btn_logout)

        //側邊欄綁定至 Toolbar
        setSupportActionBar(mToolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(false)
        supportActionBar?.setHomeButtonEnabled(true)

        actionBarDrawerToggle = object : ActionBarDrawerToggle(
            this,
            mDrawerLayout,//側邊欄的Layout
            mToolbar,//綁定側邊欄的Toolbar
            R.string.drawer_open,//字串檔新增開起字串
            R.string.drawer_close//同上
        ){  //側邊欄開啟關閉的動作
            override fun onDrawerClosed(view: View){
                super.onDrawerClosed(view)
                //當側邊欄關閉
            }

            override fun onDrawerOpened(drawerView: View){
                super.onDrawerOpened(drawerView)
                //當側邊欄開啟
            }
        }

        actionBarDrawerToggle.syncState()
        mDrawerLayout.addDrawerListener(actionBarDrawerToggle)

        //側邊欄項目(menu)點擊的動作
        mNavigationView.setNavigationItemSelectedListener { item ->
            when (item.itemId) {
                R.id.menu1 -> {
                    Toast.makeText(this@HomeActivity, "點了menu1", Toast.LENGTH_SHORT).show()
                }
                R.id.menu2 -> {
                    Toast.makeText(this@HomeActivity, "點了menu2", Toast.LENGTH_SHORT).show()
                }
            }
            item.isCheckable = false
            //點擊後關閉側邊欄
            mDrawerLayout.closeDrawer(GravityCompat.START)
            return@setNavigationItemSelectedListener true
        }

        //側邊欄Footer的元件監聽
        btnLogout.setOnClickListener {
            mDrawerLayout.closeDrawer(GravityCompat.START)
            Toast.makeText(this, "登出", Toast.LENGTH_LONG).show()
        }

        //取得側邊欄Header的元件
        val headerLayout: View = mNavigationView.getHeaderView(0)// index 0 = HeaderView

        //監聽Header的元件
        val ibtnClose: ImageButton = headerLayout.findViewById(R.id.ibtn_DrawerHeader_close)
        ibtnClose.setOnClickListener {
            mDrawerLayout.closeDrawer(GravityCompat.START)
            Toast.makeText(this, "關閉", Toast.LENGTH_LONG).show()
        }

    }
}

 

按返回鍵時優先關閉側邊欄

override fun onBackPressed() {
//        super.onBackPressed()
  //如果側邊欄開著就先關閉側邊欄
  if(dlDrawerLayout.isDrawerOpen(GravityCompat.START)){
    dlDrawerLayout.closeDrawer(GravityCompat.START)
  }else{
    finish()
  }
}

 

-待續-

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *