Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

NavigationView และ Navigation Drawer ใน Android


Navigation Drawer คืออะไร?

Navigation Drawer (หรือ Slide Menu) เป็น UI Pattern ที่ซ่อนเมนูไว้ด้านข้าง (มักเป็นซ้าย) ลากออกมาได้ โดยใช้ DrawerLayout เป็น Root View และ NavigationView สำหรับแสดงเมนู ปัจจุบันเป็นมาตรฐานของ Material Design

โครงสร้างไฟล์ Layout (สร้างโดย Android Studio Template)

1. activity_main.xml (Root Layout)

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <!-- Main Content -->
    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- Navigation Drawer (NavigationView) -->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>

2. app_bar_main.xml (Toolbar + Content)

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.Material3.Dark.ActionBar">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/ThemeOverlay.Material3.Light" />

</com.google.android.material.appbar.AppBarLayout>

<include layout="@layout/content_main" />

3. content_main.xml (เนื้อหาหลัก)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

4. nav_header_main.xml (Header ของ Drawer)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_header_height"
    android:background="@drawable/side_nav_bar"
    android:gravity="bottom"
    android:orientation="vertical"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.Material3.Dark">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:contentDescription="@string/nav_header_desc"
        app:srcCompat="@drawable/ic_account_circle" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="8dp"
        android:text="Tutorials Point"
        android:textAppearance="@style/TextAppearance.Material3.BodyLarge"
        android:textColor="#FFF"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Father of all educational website"
        android:textColor="#FFF" />

</LinearLayout>

5. menu/activity_main_drawer.xml (Menu Items)

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

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/ic_home"
            android:title="หน้าแรก" />
        <item
            android:id="@+id/nav_gallery"
            android:icon="@drawable/ic_gallery"
            android:title="แกลเลอรี" />
        <item
            android:id="@+id/nav_slideshow"
            android:icon="@drawable/ic_slideshow"
            android:title="สไลด์โชว์" />
        <item
            android:id="@+id/nav_tools"
            android:icon="@drawable/ic_tools"
            android:title="เครื่องมือ" />
    </group>

    <item android:title="ติดต่อเรา">
        <menu>
            <item
                android:id="@+id/nav_share"
                android:icon="@drawable/ic_share"
                android:title="แชร์" />
            <item
                android:id="@+id/nav_send"
                android:icon="@drawable/ic_send"
                android:title="ส่ง" />
        </menu>
    </item>

</menu>

MainActivity.java - การเชื่อมต่อและจัดการ Event

import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import com.google.android.material.navigation.NavigationView;

public class MainActivity extends AppCompatActivity 
        implements NavigationView.OnNavigationItemSelectedListener {

    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle toggle;

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

        // Toolbar
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // DrawerLayout & Toggle
        drawerLayout = findViewById(R.id.drawer_layout);
        toggle = new ActionBarDrawerToggle(
            this, drawerLayout, toolbar,
            R.string.navigation_drawer_open,
            R.string.navigation_drawer_close
        );
        drawerLayout.addDrawerListener(toggle);
        toggle.syncState();

        // NavigationView
        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.nav_home) {
            // Handle Home
        } else if (id == R.id.nav_gallery) {
            // Handle Gallery
        } else if (id == R.id.nav_slideshow) {
            // Handle Slideshow
        } else if (id == R.id.nav_tools) {
            // Handle Tools
        } else if (id == R.id.nav_share) {
            // Handle Share
        } else if (id == R.id.nav_send) {
            // Handle Send
        }

        // ปิด Drawer หลังเลือก
        drawerLayout.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        toggle.syncState(); // Sync state หลัง rotate
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        toggle.onConfigurationChanged(newConfig);
    }
}

การปรับแต่งเพิ่มเติม

เปลี่ยนตำแหน่ง Drawer เป็นขวา

<!-- ใน NavigationView -->
android:layout_gravity="end"  // แทน start

<!-- ใน Java -->
drawerLayout.closeDrawer(GravityCompat.END);

เพิ่ม Badge/Notification Count ใน Menu Item

// ใน Java
Menu menu = navigationView.getMenu();
MenuItem item = menu.findItem(R.id.nav_messages);
// ใช้ BadgeDrawable (Material Components 1.2+)
BadgeDrawable badge = BadgeDrawable.create(this);
badge.setNumber(5);
badge.setVisible(true);
item.setIcon(BadgeUtils.createBadgeDrawable(this, item.getIcon(), badge));

ใช้ Navigation Component (Modern Way)

แทนที่การจัดการ Fragment/Intent เอง ให้ใช้ NavController และ NavigationUI:

// build.gradle
implementation "androidx.navigation:navigation-fragment-ktx:2.7.0"
implementation "androidx.navigation:navigation-ui-ktx:2.7.0"

// ใน Activity
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(navigationView, navController);
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

สรุป

  • ใช้ DrawerLayout + NavigationView + Toolbar
  • Implements NavigationView.OnNavigationItemSelectedListener
  • จัดการ Back Press เพื่อปิด Drawer ก่อน
  • โปรเจกต์ใหม่ควรใช้ Navigation Component แทนการจัดการ Fragment เอง
  • รองรับ RTL อัตโนมัติ (START/END แทน LEFT/RIGHT)