Navigation Drawer là một bảng điều khiển nằm ở cạnh bên trái màn hình,nó hiển thị sự tùy chọn điều hướng của
main app.
Nó được ẩn đi trong gần như hầu hết thời gian,và được gọi khi người dùng lướt ngón tay từ bên cạnh trái màn hình vào hoặc khi người dùng ấn vào biểu tượng ứng dụng trên thanh action bar.
Bài viết sẽ mô tả làm như thế nào để triển khai một
navigation drawer sử dụng 'DrawerLayout APIs' có trong thư viện hỗ trợ 'Support Library'
Một số lưu ý về nguyên tắc thiết kế,khuôn mẫu của Navigation Drawer:
https://material.google.com/patterns/navigation-drawer.html#navigation-drawer-specs
- Tạo một Drawer Layout
- Để thêm một navigation drawer,khai báo tại giao diện người dùng một đối tượng DrawerLayout như một root view trong file layout .xml .
- Ở trong DrawerLayout,thêm một view mà chứa cái nội dung chính cho màn hình (Màn hình để hiển thị khi mà Navigation Drawer ẩn đi) và một view khác chứa nội dung của chính cái navigation drawer đấy.
- Thường sử dụng FrameLayout cho màn hình chính với vai trò là một Fragment trong khi app chạy để có thể dễ dàng thay đổi,di chuyển giữa các màn hình. Và một ListView để hiển thị nội dung cho Navigation Drawer.
<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>
Dựa vào code ở trên chúng ta có thể rút ra được một số điều quan trọng như:
- Main content view phải được khai báo như là phần tử đầu tiên của root view vì XML order được triển khai theo z-ordering,nghĩa là phần tử khai báo trước sẽ nằm ở phía sau.
- Main content view được đặt giá trị là match_parent vì nó đại diện cho toàn bộ UI khi drawer ẩn đi
- Drawer view (List view) phải được xác định trọng lực ngang của nó với thuộc tính : android:layout_gravity. Để hỗ trợ ngôn ngữ đọc từ trái sang phải,giá trị của thuộc tính này phải được đặt là 'start' thay vì 'left' cho bố cục hỗ trợ đọc từ phải sang trái.
- Drawer view được xác định chiều rộng với giá trị dp. Giá trị này không nên vượt quá 320 dp để người dùng có thể nhìn thấy một phần của main content phía sau. Thường được đặt là 240dp
- Khởi tạo Drawer List
- Trong activity,một trong những điều đầu tiên cần phải khởi tạo là list item của navigation drawer. List item này phụ thuộc vào nội dung app bạn là gì. Và nó được khởi tạo cũng như đổ dữ liệu y hệt như list view bình thường. Sử dụng Adapter (Như là ArrayAdapter hoặc là SimpleCursorAdapter)
- Dưới đây là một ví dụ khởi tạo list navigation với String Array:
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());
...
}
}
Đoạn code có phương thức setOnItemClickListener() để nhận được sự kiện click vào drawer list. Phần tiếp theo sẽ cho bạn thấy làm thế nào để triển khai giao diện và thay đổi content view khi người dùng chọn một item.
- Xử lý sự kiện click điều hướng
- Khi người dùng chọn một item trong drawer list,hệ thống gọi phương thức onItemClick() trong lớp OnItemClickListener được gán trong phương thức setOnItemClickListener(OnItemClickListener listener).
- Trong ví dụ dưới đây,mỗi khi người dùng lựa chọn một item trong list,main content view sẽ hiển thị những fragment khác nhau:
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);
}
- Lắng nghe sự kiện đóng hoặc mở
- Để lắng nghe sự kiện đóng hoặc mở navigation drawer,gọi phương thức setDrawerListener() của đối tượng DrawerLayout và gán cho nó một thực thi của lớp DrawerLayout.DrawerListener. Giao diện này sẽ cung cấp một hàm callbacks cho 2 sự kiện của drawer là 2 phương thức onDrawerOpened() và onDrawerClosed().
- Tuy nhiên,xa hơn việc thực thi lớp DrawerListener,nếu app của bạn có actionbar,bạn có thể thay thế bằng cách kế thừa lớp ActionBarDrawerToggle. Lớp này là là lớp phụ thuộc của DrawerListener nên bạn vẫn có thể override lại những callback trên,và còn có thể thực thi những hành vi tương tác giữa biểu tượng trên thanh actionbar và navigation drawer (Sẽ được trình bày sâu hơn vào phần tiếp theo)
- Như những gì đã trình bày ở bài hướng dẫn thiết kế Navigation Drawer,bạn nên tùy chỉnh nội dung của action bar khi drawer hiển thị. Vì vậy chúng ta sẽ thay đổi tên title action bar và xóa action items mà liên kết với main content đang hiển thi. Phần code dưới đây sẽ hướng dẫn bạn làm việc đó như thế nào với việc override lại các phương thức của lớp DrawerListener với một instance (thực thể) của lớp 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.drawable.ic_drawer, 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);
}
}
- Đóng hoặc mở với app icon
- Phần này sẽ mô tả tham số khởi tạo của lớp ActionBarDrawerToggle và những bước cần thiết để cài đặt nhằm xử lý những tương tác giữa icon của action bar và Navigation Drawer
- Người dùng có thể mở và đóng navigation drawer với thao tác lướt nhưng nếu app của bạn sử dụng action bar,bạn nên cho phép người dùng mở và đóng navigation bar bằng cách chạm vào biểu tượng trên thanh action bar. Và biểu tượng app này cũng nên biểu thị sự hiện diện của navigation drawer với biểu tượng đặc biệt (3 dấu * dọc nhau). Bạn có thể thực hiện tất cả các hành vi này bằng việc sử dụng ActionBarDrawerToggle mà đã được giới thiệu ở phần trước.
- Để có thể sử dụng được ActionBarDrawerToggle,bạn cần tạo một instance của nó với hàm khởi tạo cùng với những tham số cần thiết sau
- Activity chủ của drawer
- DrawerLayout (tất nhiên)
- Drawable resource để sử dụng làm icon đại diện cho drawer
- 1 chuỗi String để mô tả cho hành động 'open drawer'
- 1 chuỗi String để mô tả cho hành động 'close drawer'
- Bây giờ thì có hoặc không bạn tạo một subclass của ACtionBarDrawerToggle như một listener cho drawer của bạn,bạn cần gọi ActionBarDrawerToggle trong một vài chỗ xuyên suốt vòng đời activity của bạn
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.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
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);
}
...
}