Membuat Panel Samping Navigasi

Panel samping navigasi adalah panel yang menampilkan opsi navigasi utama aplikasi di tepi kiri layar. Pada sebagian besar waktu, panel ini tersembunyi, namun ditampilkan saat pengguna menggesek dari tepi kiri layar, atau saat berada pada level atas aplikasi, pengguna menyentuh ikon aplikasi di bilah aksi.

Pelajaran ini menjelaskan cara menerapkan panel samping navigasi menggunakan DrawerLayout API yang tersedia di Pustaka Dukungan.

Desain Panel Samping Navigasi

Sebelum memutuskan untuk menggunakan panel samping navigasi pada aplikasi Anda, Anda harus memahami kasus penggunaan dan prinsip desain yang didefinisikan dalam panduan desain Panel Samping Navigasi.

Membuat Layout Panel Samping

Untuk menambahkan panel samping navigasi, nyatakan antarmuka pengguna Anda dengan objekDrawerLayout sebagai tampilan akar layout Anda. Di dalam DrawerLayout, tambahkan satu tampilan yang berisi materi utama untuk layar (layout utama Anda saat panel samping tersembunyi) dan tampilan lain yang berisi materi panel samping navigasi.

Misalnya, layout berikut menggunakan DrawerLayout dengan tampilan dua anak: FrameLayout untuk menampung materi utama (diisi oleh Fragment di waktu proses), dan ListView untuk panel samping navigasi.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!-- The navigation drawer -->
    <ListView android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</android.support.v4.widget.DrawerLayout>

Layout ini menunjukkan beberapa karakteristik layout penting:

  • Tampilan materi utama ( FrameLayout di atas) harus anak pertama di DrawerLayout karena urutan XML menyiratkan pengurutan-z dan panel samping harus berada di bagian atas materi.
  • Tampilan materi utama disetel untuk sesuai dengan lebar dan tinggi tampilan induk karena mewakili seluruh UI saat panel samping navigasi tersembunyi.
  • Tampilan panel samping (ListView) harus menetapkan gravitasi horizontal dengan atribut android:layout_gravity. Untuk mendukung bahasa kanan-ke-kiri (right-to-left - RTL), tetapkan nilai "start" bukan "left" (agar panel samping muncul di kanan saat layout RTL).

  • Tampilan panel samping menetapkan lebarnya dalam satuan dp dan tinggi sesuai dengan tampilan induk. Lebar panel samping tidak boleh lebih dari 320 dp sehingga pengguna selalu dapat melihat bagian dari materi utama.

Memulai Daftar Panel Samping

Pada aktivitas Anda, salah satu hal pertama yang perlu dilakukan adalah memulai daftar item panel samping navigasi. Cara Anda melakukannya tergantung pada materi aplikasi Anda, namun panel samping navigasi sering kali mengandung ListView, sehingga daftar harus diisi olehAdapter (seperti ArrayAdapter atau SimpleCursorAdapter).

Misalnya, berikut cara Anda dapat memulai daftar navigasi dengan larik string:

public class MainActivity extends Activity {
    private String[] mPlanetTitles;
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    ...

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

        mPlanetTitles = getResources().getStringArray(R.array.planets_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // Set the adapter for the list view
        mDrawerList.setAdapter(new ArrayAdapter<String>(this,
                R.layout.drawer_list_item, mPlanetTitles));
        // Set the list's click listener
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        ...
    }
}

Kode ini juga memanggil setOnItemClickListener() untuk menerima kejadian klik di daftar panel samping navigasi. Bagian berikutnya menunjukkan cara menerapkan antarmuka ini dan mengubah tampilan materi saat pengguna memilih item.

Menangani Kejadian Klik Navigasi

Bila pengguna memilih item di daftar panel samping, sistem akan memanggil onItemClick() di OnItemClickListener yang diberikan kepada setOnItemClickListener().

Apa yang Anda lakukan dalam metode onItemClick() tergantung pada cara Anda menerapkan struktur aplikasi. Dalam contoh berikut, memilih masing-masing item di daftar menyisipkan Fragment yang berbeda ke dalam tampilan materi utama (elemen FrameLayout yang diidentifikasi sebagai ID R.id.content_frame):

private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectItem(position);
    }
}

/** Swaps fragments in the main content view */
private void selectItem(int position) {
    // Create a new fragment and specify the planet to show based on position
    Fragment fragment = new PlanetFragment();
    Bundle args = new Bundle();
    args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
    fragment.setArguments(args);

    // Insert the fragment by replacing any existing fragment
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction()
                   .replace(R.id.content_frame, fragment)
                   .commit();

    // Highlight the selected item, update the title, and close the drawer
    mDrawerList.setItemChecked(position, true);
    setTitle(mPlanetTitles[position]);
    mDrawerLayout.closeDrawer(mDrawerList);
}

@Override
public void setTitle(CharSequence title) {
    mTitle = title;
    getActionBar().setTitle(mTitle);
}

Mendengarkan Kejadian Terbuka dan Tertutup

Untuk mendengarkan kejadian terbuka dan tertutup panel samping, panggil setDrawerListener() di DrawerLayout dan sampaikan sebagai penerapan di DrawerLayout.DrawerListener. Antarmuka ini menyediakan callback untuk kejadian panel samping seperti onDrawerOpened() dan onDrawerClosed().

Namun demikian, ketimbang menerapkan DrawerLayout.DrawerListener, jika aktivitas Anda termasuk bilah aksi, Anda dapat memperpanjang kelas ActionBarDrawerToggle. ActionBarDrawerToggle menerapkan DrawerLayout.DrawerListener sehingga Anda masih dapat menggantikan callback tersebut, tetapi juga memfasilitasi perilaku interaksi patut antara ikon bilah aksi dan panel samping navigasi (dibahas lebih lanjut di bagian berikutnya).

Sesuai yang dibahas dalam panduan desain Panel Samping Navigasi, Anda harus memodifikasi materi bilah aksi saat panel samping terlihat, misalnya untuk mengubah judul dan membuang item aksi yang kontekstual terhadap materi utama. Kode berikut menampilkan cara Anda dapat mengganti metode callback DrawerLayout.DrawerListener dengan instance kelas ActionBarDrawerToggle:

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    ...

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

        mTitle = mDrawerTitle = getTitle();
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
                R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

    /* Called whenever we call invalidateOptionsMenu() */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // If the nav drawer is open, hide action items related to the content view
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }
}

Bagian berikutnya menjelaskan argumen konstruktor ActionBarDrawerToggle dan langkah lainnya yang diperlukan untuk menyiapkannya untuk menangani interaksi dengan ikon bilah aksi.

Membuka dan Menutup Ikon Aplikasi

Pengguna dapat membuka dan menutup panel samping navigasi dengan isyarat gesek dari atau menuju samping kiri layar, namun jika Anda menggunakan bilah aksi, Anda juga harus memperbolehkan pengguna membuka dan menutupnya dengan menyentuh ikon aplikasi. Dan ikon aplikasi harus juga menunjukkan adanya panel samping navigasi dengan ikon khusus. Anda dapat menerapkan semua perilaku ini menggunakan ActionBarDrawerToggle yang ditampilkan di bagian sebelumnya.

Untuk memfungsikan ActionBarDrawerToggle, buat instance dengan konstruktornya yang memerlukan argumen berikut:

  • Activity meng-hosting panel samping.
  • DrawerLayout.
  • Sumber daya dapat digambar untuk digunakan sebagai indikator panel samping.

    Ikon panel samping navigasi standar tersedia di Unduh Paket Ikon Bilah Aksi.

  • Sumber daya String untuk menjelaskan aksi "open drawer" (untuk aksesibilitas).
  • Sumber daya String untuk menjelaskan aksi "close drawer" (untuk aksesibilitas).

Lalu, apakah Anda membuat subkelas ActionBarDrawerToggle atau tidak sebagai pendengar panel samping Anda, Anda perlu memanggil ActionBarDrawerToggle di beberapa tempat di seluruh daur hidup aktivitas Anda:

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    ...

    public void onCreate(Bundle savedInstanceState) {
        ...

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.string.drawer_open,  /* "open drawer" description */
                R.string.drawer_close  /* "close drawer" description */
                ) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle.onOptionsItemSelected(item)) {
          return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }

    ...
}

Untuk contoh lengkap panel samping navigasi, unduh sampel yang tersedia di bagian atas laman.