Trong bài hướng dẫn này, chúng ta sẽ tìm hiểu cách tạo Navigation Drawer trong Android. Đây là một thành phần UI quan trọng thường gặp trong hầu hết các ứng dụng Android. Nó có chức năng tương tự như các thanh menu điều hướng trên các trang web.
Navigation Drawer trong Android
Đây là một menu trượt từ bên trái dùng để hiển thị các liên kết quan trọng trong ứng dụng. Nó giúp người dùng dễ dàng di chuyển qua lại giữa các liên kết này. Mặc định nó được ẩn đi và chỉ hiện ra khi người dùng chạm trượt từ cạnh trái màn hình hoặc nhấn vào icon của nó trên ActionBar.
Nói một cách tổng quát, Navigation Drawer là một bảng điều khiển dạng lớp phủ (overlay panel). Nó thay thế cho một màn hình activity thường chỉ dùng để hiển thị tất cả các tùy chọn và liên kết của ứng dụng.
Trong bài hướng dẫn này, chúng ta sẽ triển khai Navigation Drawer bằng Drawer Layout API trong Android Support Library. Bài viết sẽ trình bày 3 giao diện fragment(các thành phần UI độc lập) có thể mở được từ các mục của drawer.
Cấu trúc project ví dụ
Ví dụ sử dụng Navigation Drawer trong Android
Để triển khai Navigation Drawer, trước tiên chúng ta cần thêm android.support.v4.widget.DrawerLayout
làm phần tử gốc (root) cho layout của activity như ở dưới đây.
File activity_main.xml
:
<android.support.v4.widget.DrawerLayout
xmlns:android="<https://schemas.android.com/apk/res/android>"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/container_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include
android:id="@+id/toolbar"
layout="@layout/toolbar" />
</LinearLayout>
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<ListView
android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFFFFF"
android:choiceMode="singleChoice"
android:divider="@android:color/darker_gray"
android:dividerHeight="1dp" />
</android.support.v4.widget.DrawerLayout>
Các tùy chọn menu trong navigation drawer được lưu trữ dưới dạng một ListView. Mỗi tùy chọn sẽ mở nội dung trong một FrameLayout.
Ở đây, chúng ta sử dụng ToolBar thay cho ActionBar. ToolBar đã xuất hiện từ Android 5.0 như một dạng tổng quát hơn của ActionBar. Nó giúp ta kiểm soát và tùy chỉnh linh hoạt hơn, đồng thời cũng dễ dàng tích hợp với các view khác trong cấu trúc view.
Layout cho ToolBar được định nghĩa trong file layout toolbar.xml
dưới đây.
<android.support.v7.widget.Toolbar xmlns:android="<https://schemas.android.com/apk/res/android>"
xmlns:local="<https://schemas.android.com/apk/res-auto>"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
Khi sử dụng Toolbar, ta cần dùng Theme Theme.AppCompat.NoActionBar
trong styles.xml
. Layout cho các dòng của ListView trong Navigation Drawer nằm ở file list_view_item_row.xml
.
<RelativeLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:padding="10dp" >
<ImageView
android:id="@+id/imageViewIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:paddingRight="10dp" />
<TextView
android:id="@+id/textViewName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/imageViewIcon"
android:paddingRight="10dp"
android:text="Item Name"
android:textColor="@android:color/black"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
/>
</RelativeLayout>
Các mục của navigation drawer được khai báo trong một mảng string tại file strings.xml
.
<string-array name="navigation_drawer_items_array">
<item>Connect</item>
<item>Fixtures</item>
<item>Table</item>
</string-array>
Class DataModel.java
được dùng để định nghĩa đối tượng cho các mục trong danh sách của drawer.
package com.journaldev.navigationdrawer;
public class DataModel {
public int icon;
public String name;
// Constructor.
public DataModel(int icon, String name) {
this.icon = icon;
this.name = name;
}
}
Các mục của drawer được lưu trữ dưới dạng một ListView. Do đó, chúng ta cần sử dụng một class Adapter để cung cấp dữ liệu này cho class Activity.
File DrawerItemCustomAdapter.java
:
package com.journaldev.navigationdrawer;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class DrawerItemCustomAdapter extends ArrayAdapter<DataModel> {
Context mContext;
int layoutResourceId;
DataModel data[] = null;
public DrawerItemCustomAdapter(Context mContext, int layoutResourceId, DataModel[] data) {
super(mContext, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.mContext = mContext;
this.data = data;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View listItem = convertView;
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
listItem = inflater.inflate(layoutResourceId, parent, false);
ImageView imageViewIcon = (ImageView) listItem.findViewById(R.id.imageViewIcon);
TextView textViewName = (TextView) listItem.findViewById(R.id.textViewName);
DataModel folder = data[position];
imageViewIcon.setImageResource(folder.icon);
textViewName.setText(folder.name);
return listItem;
}
}
Nội dung file MainActivity.java
giống như ở dưới:
package com.journaldev.navigationdrawer;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
private String[] mNavigationDrawerItemTitles;
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
Toolbar toolbar;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
android.support.v7.app.ActionBarDrawerToggle mDrawerToggle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTitle = mDrawerTitle = getTitle();
mNavigationDrawerItemTitles= getResources().getStringArray(R.array.navigation_drawer_items_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
setupToolbar();
DataModel[] drawerItem = new DataModel[3];
drawerItem[0] = new DataModel(R.drawable.connect, "Connect");
drawerItem[1] = new DataModel(R.drawable.fixtures, "Fixtures");
drawerItem[2] = new DataModel(R.drawable.table, "Table");
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(true);
DrawerItemCustomAdapter adapter = new DrawerItemCustomAdapter(this, R.layout.list_view_item_row, drawerItem);
mDrawerList.setAdapter(adapter);
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setDrawerListener(mDrawerToggle);
setupDrawerToggle();
}
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new ConnectFragment();
break;
case 1:
fragment = new FixturesFragment();
break;
case 2:
fragment = new TableFragment();
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
mDrawerList.setItemChecked(position, true);
mDrawerList.setSelection(position);
setTitle(mNavigationDrawerItemTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
} else {
Log.e("MainActivity", "Error in creating fragment");
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getSupportActionBar().setTitle(mTitle);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mDrawerToggle.syncState();
}
void setupToolbar(){
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
void setupDrawerToggle(){
mDrawerToggle = new android.support.v7.app.ActionBarDrawerToggle(this,mDrawerLayout,toolbar,R.string.app_name, R.string.app_name);
//This is necessary to change the icon of the Drawer Toggle upon state change.
mDrawerToggle.syncState();
}
}
Trong đoạn code trên, getSupportActionBar().setDisplayHomeAsUpEnabled(false);
được dùng để ẩn nút back (trở về) mặc định. Chúng ta đã sử dụng một class DrawerItemClickListener
để tải Fragment tương ứng với mục trong danh sách được chọn, thông qua một FragmentManager.
Đồng thời, tiêu đề của ToolBar cũng được cập nhật theo mục được chọn thông qua setTitle(mNavigationDrawerItemTitles[position]);
. Các class Fragment và các layout tương ứng của chúng được trình bày dưới đây.
File ConnectFragment.java
:
package com.journaldev.navigationdrawer;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class ConnectFragment extends Fragment {
public ConnectFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_connect, container, false);
return rootView;
}
}
Layout của fragment nêu trên được định nghĩa như trong file fragment_connect.xml
dưới đây.
<RelativeLayout xmlns:android="<https://schemas.android.com/apk/res/android>"
xmlns:tools="<https://schemas.android.com/tools>"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/label"
android:layout_alignParentTop="true"
android:layout_marginTop="100dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textSize="45dp"
android:text="Connect"
android:textStyle="bold"/>
<TextView
android:layout_below="@id/label"
android:layout_centerInParent="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="12dp"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:text="Edit fragment_connect.xml to change the appearance"
android:id="@+id/textView2" />
</RelativeLayout>
Hai mục còn lại được định nghĩa hoàn toàn tương tự như trên, do đó chúng ta sẽ không mô tả chi tiết chúng ở đây.
Kết quả của ví dụ Navigation Drawer
Dưới đây là kết quả chạy thử ứng dụng ví dụ về Navigation Drawer ở trên.
Tổng kết
Bài hướng dẫn về ví dụ tạo Navigation Drawer trong Android đến đây là kết thúc. Bạn có thể tải code của project mẫu ở link này. Mong gặp lại bạn ở những bài tutorial về Android khác trong tương lai!