Android Bài 25: Xây Dựng Navigation Drawer

Posted by

Chào mừng các bạn đã đến với bài học Android số 25, bài học về cách xây dựng Navigation Drawer. Đây là bài học trong chuỗi bài về lập trình ứng dụng Android bằng Java của Yellow Code Books.

Với bài học hôm trước, bạn đã “nâng cấp” một tí cho TourNote thông qua việc tạo cho ứng dụng này một ActionBar. Hôm nay chúng ta lại sẽ nâng cái cấp này lên một tí ti nữa, thông qua việc xây dựng thêm cho nó một Navigation Drawer.

Giới Thiệu Về Navigation Drawer

Có lẽ cái tên Navigation Drawer, hay nhiều bạn vẫn gọi là Left Menu, hoặc Slide Menu, không có gì xa lạ với chúng ta cả. Ai cũng biết về nó, ai cũng sử dụng nó hằng ngày. Navigation Drawer được Google giới thiệu vào năm 2013, ngay sau khi ActionBar được trình làng khoảng 2 năm. Navigation Drawer này chỉ đơn giản là một thanh menu được ẩn đi về phía bên trái màn hình. Nó được hiển thị ra khi người dùng nhấn vào icon menu trên Action Bar. Giới thiết kế gọi cái icon menu này bằng một cái tên khá hay: “icon Hamburger”, bởi vì trông nó giống như một cái hamburger… bạn tưởng tượng đi nào.

Navigation Drawer icon trông như cái bánh hambuger

Cũng giống như ActionBar, Navigation Drawer mong muốn đem lại cho user một trải nghiệm rõ ràng hơn trên các ứng dụng phức tạp. Khi đó các chức năng chính của ứng dụng dường như được để hết cả lên thanh này (và cả ở ActionBar nữa, nhưng thường thì ActionBar chỉ chứa các chức năng quan trọng và được truy xuất cực kỳ nhiều mà thôi). Và cũng không quá khi nói rằng Navigation Drawer này mới chính thức là “xương sống” rõ ràng nhất cho ứng dụng.

Navigation Drawer mẫu

Ngoài các thông tin về Navigation Drawer của mình trên đây, bạn có thể vào link này của Google để đọc một số thông tin hữu ích về mặt thiết kế liên quan đến thành phần này nhé.

Tìm Hiểu Các Layout Và Thành Phần Mới

Nếu như bạn còn nhớ, mình đã dành một bài để nói nhiều nhất có thể các layout được dùng phổ biến trong Android, trong bài viết đó, FrameLayout, LinearLayout, RelativeLayoutTableLayout đã được nhắc đến. Và cuối bài viết, mình có nói rằng là sẽ còn nhiều layout khác liên quan đến các giao diện đặc trưng khác chứ không riêng gì các layout phổ biến này. Và đến bài học hôm nay bạn đã có dịp làm quen với một trong số các layout đặc trưng cho Navigation Drawer. Mình xin giới thiệu, một layout mới mang tên DrawerLayout.

DrawerLayout

Như đã nói, DrawerLayout là một layout đặc biệt, nó chuyên dùng để tạo Navigation Drawer. Layout này nằm trong gói support-v4support-v13 để hỗ trợ sự tương thích ngược đến với các hệ điều hành cũ hơn. Nếu bạn thắc mắc tại sao lại có nhiều gói support-vx này thì hãy yên tâm, mình dự định dành một bài viết riêng để nói rõ về các thư viện support này nhé.

Và bởi vì layout này đặc biệt là dùng kèm theo Navigation Drawer, nên nó hầu như chẳng có nhiều nguyên tắc lắm đâu. Việc của bạn chỉ cần khai báo một DrawerLayout ở gốc của một Activity nào đó, rồi khai báo bên trong layout đó hai thành phần con, đại loại như thế này, là được.

<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" >

    <!-- Thành phần con thứ nhất -->

    <!-- Thành phần con thứ hai -->

</android.support.v4.widget.DrawerLayout>

Mình xin nói rõ một chút về vai trò của hai thành phần con trong DrawerLayout.

Thành phần con thứ nhất, chính là giao diện chính của ứng dụng. Một lát nữa khi thực hành xây dựng ứng dụng TourNote, bạn phải thay đổi một chút cấu trúc của màn hình chính TourNote, sao cho trở thành thành phần con thứ nhất này.
Thành phần con thứ hai, chính là thành phần của left menu. Thành phần này như đã nói, bình thường sẽ bị ẩn đi, đến khi người dùng nhấn vào icon hamburger hoặc vuốt từ bên trái màn hình sang phải, sẽ được hiển thị. Sau đó nếu người dùng nhấn lại vào icon hamburger, hoặc nhấn vào vùng ngoài left menu, hoặc vuốt từ bên phải màn hình sang trái, thì lại ẩn đi.

Chúng ta sẽ hiểu rõ hơn về DrawerLayout và từng thành phần con của nó khi tiến hành xây dựng Navigation Drawer cho TourNote ở bên dưới đây.

Xây Dựng Navigation Drawer

Trước khi đi vào chi tiết cách xây dựng Navigation Drawer, mình xin được nói rõ rằng, sẽ có hai cách để bạn có thể xây dựng thành phần này cho ứng dụng.

Cách thứ nhất, chẳng tốn công sức gì cả, khi bạn tạo mới bất kỳ project Android nào, đến bước chọn một template (bạn có thể xem lại bài 3 để hiểu các khái niệm liên quan đến tạo mới một project Android), thì bạn cứ chọn Navigation Drawer Activity như hình dưới. Tùy chọn này giúp bạn tạo một ứng dụng có cả Navigation Drawer lẫn Floating Action Button. Thật là đơn giản.

Navigation Drawer Activity lúc tạo mới project

Cách thứ hai, là cách bạn phải bỏ công sức ra xây dựng từng bước như dưới đây chúng ta sẽ làm với TourNote. Mình thích cách trên kia, nhưng với các bài học của mình thì mình muốn cùng các bạn đi theo hướng gian khổ ở cách này. Bởi vì sau khi xây dựng thủ công mọi thứ, chắc chắn các bạn sẽ hiểu rõ hơn về thành phần giao diện này, và các kiến thức liên quan. Sau đó, tự bạn có thể tùy chỉnh, thêm thắt vài thứ vào giao diện một cách dễ dàng hơn.

Thực Hành Xây Dựng Navigation Drawer Cho TourNote

Đây là thiết kế ban đầu về Navigation Drawer của TourNote.

Navigation Drawer thiết kế ban đầu

Như những gì bạn đã đọc qua trên đây, chắc bạn cũng đã hình dung ra các bước mà bài thực hành này sẽ xây dựng. Bao gồm, thay đổi activity_main.xml sao cho layout gốc chính là DrawerLayout nè, rồi xây dựng các thành phần bên trong DrawerLayout nè, và cuối cùng là xây dựng các Java code liên quan nè. Chúng ta cùng vào chi tiết.

Download Resource

Do có xuất hiện thêm một vài icon nữa, bao gồm icon hình dấu chấm than và chấm hỏi, nên bạn có thể vào Android Asset Studio để tự tìm hai icon này.

Navigation Drawer icon từ Android Asset Studio

Hoặc bạn có thể vào link này để down file zip về rồi giải nén và để các ảnh vừa down vào các alternative resource tương ứng nhé.

Thêm Một Số Thông Số Vào dimens.xml

Chúng ta sẽ tuân thủ các yêu cầu về mặt thiết kế ở link này của Google. Nên bước này chúng ta nên định nghĩa thêm một số kích thước cho Navigation Drawer vào file dimens.xml, bạn chú ý các dòng được tô sáng.

<resources>

    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>

    <!-- Padding -->
    <dimen name="padding_tiny">2dp</dimen>
    <dimen name="padding_tiny_plus_one">3dp</dimen>
    <dimen name="padding_small">4dp</dimen>
    <dimen name="padding_medium">8dp</dimen>
    <dimen name="padding_large">16dp</dimen>
    <dimen name="padding_extra_large">24dp</dimen>
    <dimen name="padding_extra_extra_large">32dp</dimen>

    <!-- Main Activity components -->
    <dimen name="empty_icon_width">60dp</dimen>
    <dimen name="empty_icon_height">60dp</dimen>
    <dimen name="navigation_header_height">160dp</dimen>
    <dimen name="navigation_item_height">48dp</dimen>
    <dimen name="navigation_item_icon_size">24dp</dimen>

</resources>

Xây Dựng DrawerLayout

Chúng ta cùng mở lại giao diện chính của TourNote ra, chính là file activity_main.xml. Tại đây, như các bài thực hành từ trước giờ, thẻ gốc của file này vẫn là RelativeLayout (hoặc bạn đã tự chỉnh sửa thành ConstraintLayout rồi cũng không sao).

Với thay đổi ở bước này, bạn chỉ chuyển toàn bộ giao diện gốc của TourNote này thành thành phần thứ nhất. Như vậy giao diện gốc xưa kia giờ đây nằm trong một thẻ có tên android.support.v4.widget.DrawerLayout. Bạn chú ý ghi đầy đủ đường dẫn đến gói support.v4.widget luôn nhé, vì đây là layout được lấy từ thư viện support như mình có nói đến trên kia mà.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.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/activity_main_drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Content -->

    <RelativeLayout
        android:id="@+id/activity_main_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.yellowcode.tournote.MainActivity">

        <TextView
            android:id="@+id/activity_main_tv_empty"
            style="@style/InformationTextView"
            android:layout_centerInParent="true"
            android:text="@string/mainscreen_empty_note" />

        <ImageView
            android:layout_width="@dimen/empty_icon_width"
            android:layout_height="@dimen/empty_icon_height"
            android:layout_below="@id/activity_main_tv_empty"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="@dimen/padding_medium"
            android:scaleType="fitCenter"
            android:src="@drawable/empty_note" />

    </RelativeLayout>

</android.support.v4.widget.DrawerLayout>

Bạn khoan run ứng dụng lên vội, vì chưa có thay đổi gì đâu.

Xây Dựng Thành Phần Con Thứ Hai

Bạn đã biết thành phần con thứ nhất trong DrawerLayout chính là RelativeLayout cũ được chúng ta nhét vào trong DrawerLayout rồi đúng không nào. Vậy nên bước này chúng ta chỉ cần xây dựng thành phần con thứ hai, chính là giao diện cho left menu. Dưới đây là một vài ý quan trọng cho thành phần này.

– Với layout ở thành phần con thứ hai này, bạn xây dựng chúng bằng bất cứ layout nào cũng được. Nhưng mình khuyên bạn nên dùng NavigationView. Đây cũng là một layout được chuyên dùng trong Navigation Drawer, nó giúp định nghĩa ra một menu layout chuẩn, với các màu sắc và kích cỡ mặc định, và khi sử dụng nó, bạn không cần phải lo lắng canh chỉnh chiều rộng của left menu này như thế nào.
– Bởi vì NavigationView là con cháu của FrameLayout nên nó không giỏi lắm trong việc sắp xếp các thành phần UI con vào trong nó. Tốt nhất bạn nên xây dựng thêm một layout linh động hơn vào con của NavigationView này. Như code dưới đây mình thêm LinearLayout.
– Dù bạn có dùng layout nào cho thành phần này, cũng đừng quên khai báo thuộc tính android:layout_gravity=”start”, nó giúp neo layout này vào bên trái của thành phần con thứ nhất trên kia.

Và đây là code của riêng left menu, code này nằm dưới đoạn code vừa định nghĩa ở trên.

<!-- Left Menu -->

<android.support.design.widget.NavigationView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:orientation="vertical">

        <!-- Header -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/navigation_header_height"
            android:background="@color/colorPrimary"
            android:gravity="bottom"
            android:orientation="vertical"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            android:theme="@style/ThemeOverlay.AppCompat.Dark">

            <ImageView
                android:id="@+id/activity_main_imv_avatar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="@dimen/activity_vertical_margin"
                app:srcCompat="@mipmap/ic_launcher_round" />

            <TextView
                android:id="@+id/activity_main_tv_user_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingTop="@dimen/activity_vertical_margin"
                android:text="Yellow Code"
                android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

            <TextView
                android:id="@+id/activity_main_tv_email"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="yellowcode.books@gmail.com" />

        </LinearLayout>

        <!-- Item Info -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/navigation_item_height"
            android:layout_marginTop="@dimen/padding_tiny_plus_one"
            android:background="@android:color/white"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin">

            <ImageView
                android:layout_width="@dimen/navigation_item_icon_size"
                android:layout_height="@dimen/navigation_item_icon_size"
                android:src="@drawable/ic_action_info" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/padding_extra_extra_large"
                android:text="@string/menu_item_about_app" />

        </LinearLayout>

        <!-- Item Help -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/navigation_item_height"
            android:background="@android:color/white"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin">

            <ImageView
                android:layout_width="@dimen/navigation_item_icon_size"
                android:layout_height="@dimen/navigation_item_icon_size"
                android:src="@drawable/ic_action_help" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/padding_extra_extra_large"
                android:text="@string/menu_item_help" />

        </LinearLayout>

    </LinearLayout>

</android.support.design.widget.NavigationView>

Chú ý rằng bên trong NavigationView này mình đã lồng vào một LinearLayout với mục đích như đã nói đến trên đây. Bên trong LinearLayout này mình xây dựng một header để hiển thị thông tin người dùng. Phía dưới header là hai item của left menu, đó là item info và item help. Hai item này tốt nhất nên nằm trong một ListView, nhưng vì chúng ta chưa được học qua ListView, nên mình chỉ xây dựng tạm như thế này để nhìn cho đẹp mắt thôi chứ chưa có logic gì cho việc click lên ẻm đâu nhá.

Chỉnh Sửa MainActivity.java

Đến bước này, nếu bạn thực thi chương trình, cũng sẽ không có bất cứ giao diện cho left menu nào xuất hiện đâu nhé. Thực chất chúng đã nằm sẵn ở bên tay trái màn hình thiết bị nhờ vào việc sắp xếp của DrawerLayout. Việc tiếp theo của chúng ta là xây dựng một số hành động bên MainActivity.java nữa để menu này có cơ hội được xuất hiện.

Thứ nhất, bạn phải đảm bảo cho icon hamburger xuất hiện trên ActionBar bằng hai dòng lệnh sau.

public class MainActivity extends AppCompatActivity {

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

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_actions, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.search:
                Toast.makeText(this, "Search button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.about:
                Toast.makeText(this, "About button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.help:
                Toast.makeText(this, "Help button selected", Toast.LENGTH_SHORT).show();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Sau đó, dĩ nhiên rồi, bạn khai báo DrawerLayout trong Java code, để thiết lập vài logic cho nó.

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;

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

        drawerLayout = (DrawerLayout) findViewById(R.id.activity_main_drawer);

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


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_actions, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.search:
                Toast.makeText(this, "Search button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.about:
                Toast.makeText(this, "About button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.help:
                Toast.makeText(this, "Help button selected", Toast.LENGTH_SHORT).show();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Cuối cùng, chúng ta lại dùng đến một thành phần có tên là ActionBarDrawerToggle. Thành phần này được gắn vào ActionBar, làm nhiệm vụ điều khiển việc đóng mở DrawerLayout cho chúng ta. Sau đây là các đoạn code khai báo, gắn vào ActionBar, và đồng bộ thành phần ActionBarDrawerToggle này với Activity. Đoạn code khai báo sau cần sự định nghĩa thêm hai resource string, đó là navigation_drawer_open và navigation_drawer_close, bạn có thể xem thêm định nghĩa string này ở link project ở cuối bài học hôm nay.

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle drawerToggle;

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

        drawerLayout = (DrawerLayout) findViewById(R.id.activity_main_drawer);
        drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawerLayout.addDrawerListener(drawerToggle);

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

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

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_actions, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(drawerToggle.onOptionsItemSelected(item)) {
            return true;
        }

        switch (item.getItemId()) {
            case R.id.search:
                Toast.makeText(this, "Search button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.about:
                Toast.makeText(this, "About button selected", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.help:
                Toast.makeText(this, "Help button selected", Toast.LENGTH_SHORT).show();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Giờ thì bạn có thể thực thi ứng dụng lên xem được rồi nhé.Navigation Drawer kết quả cuối cùng

Download Source Code Mẫu

Bạn có thể download source code mẫu của bài này ở đây.

Vậy là chúng ta vừa xem xong cách thức xây dựng một Navigation Drawer cho TourNote. Bạn đừng nên học thuộc lòng các bước xây dựng giao diện trong bài hôm nay. Nội dung bài học được mình tham khảo ở link gốc này của Google, và nó cũng không hoàn toàn khớp với link gốc, nó dựa trên sự tham khảo và tổng hợp từ nhiều nguồn khác nhau. Quan trọng là bạn hiểu các thành phần và tìm cách kết hợp chúng với nhau là được rồi.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Trước giờ chúng ta đã làm quen nhiều cách thao tác ở MainActivity.java. Và bạn cũng đã biết một Activity như vậy chịu trách nhiệm hiển thị một giao diện lên màn hình thiết bị rồi đúng không nào. Vậy thì còn việc hiển thị nhiều màn hình trong một ứng dụng thì sao. Và còn bất kỳ thông tin nào khác liên quan đến khái niệm Activity hay không. Bài sau chúng ta cùng tìm hiểu qua nhé.

Advertisements
Rating: 5.0/5. From 10 votes.
Please wait...

14 comments

    1. Bạn Tuấn ơi. Code này của mình chưa tốt, nên mình chưa làm gì với sự kiện nhấn trên item left menu cả. Tuy nhiên bạn vẫn có thể set click listener lên từng widget hoặc layout để bắt sự kiện nhấn của user (bạn có thể xem việc bắt sự kiện click ở đây https://yellowcodebooks.com/2016/11/08/tim-hieu-cac-widget-co-ban/#Nhan_Su_Kien_Qua_Viec_Lang_Nghe_OnClickListener) rồi khi user nhấn vào đó bạn có thể mở Activity khác chẳng hạn (bạn xem ở bài 26 về thao tác với Activity). Nhưng như mình có nói, sau này khi xây dựng left menu là ListView sẽ dễ thao tác hơn, và mình dành sự nâng cấp này cho các bài sau. Nếu bạn có thắc mắc gì thêm thì có thể gởi mail cho mình nhé (yellowcode.books@gmail.com).

  1. A cho e hỏi e làm Switch trong DrawerLayout. Nhưng Switch đó nằm trong 1 item (activelayout = switch). Lúc làm sự kiện thì chỉ find id đc của cái item chứa Switch đó. Bây giờ e muốn tìm đến cái switch đó để viết sự kiện khi thay đổi trạng thái thì phải làm thế nào ạ? (Sự kiện cho item chứa switch đó thì làm vẫn ok ạ, nhưng chưa làm đc sự kiện khi switch change trạng thái
    )

    1. Chào bạn, thực sự mình đã thử xem vấn đề của bạn là gì nhưng vẫn chưa thực sự hiểu lắm, bạn có thể liên lạc với mình qua chat trên facebook Yellow Code Books để mình dễ xác định vấn đề hơn nhé.

  2. Bạn cho mình hỏi trong cái ActionBarDrawerToggle(….) co 2 thông số là string open và close,mình chưa hiểu chổ string đó có tác dụng ji

    1. Well, cái vụ này, thực ra mình chưa được biết rõ tường tận. Nhưng theo mình đã từng tìm hiểu thì, một số component của Android mà user không thể đọc được, như Image, và bây giờ bạn biết thêm có việc open và close của Navigation Drawer. Các component này đòi hỏi các developer chúng ta phải định nghĩa description cho nó. Để làm chi? Nó có thể dùng trong một số chức năng muốn hỗ trợ các người dùng đặc biệt, một trong những chức năng đó là text-to-speech, tức là hệ điều hành sẽ đọc to lên đoạn text mà developer chúng ta đã định nghĩa.

  3. Cho mình hỏi đoạn code trong method onPostCreate và onConfigurationChanged có tác dụng gì vậy? Và khi nào thì 2 method đó được gọi ạ?

    1. Hai phương thức override onPostCreate() và onConfigurationChanged() ở bài học hôm nay là do mình copy từ đoạn code mẫu trên trang Android Developer. Tuy nhiên chúng ta cũng nên hiểu là onPostCreate() thì thường dùng cho hệ thống hơn, ở đây chúng ta override phương thức này để gọi phương thức đồng bộ của drawerToggle ngay khi Activity được tạo xong, một yêu cầu của Navigation Drawer mà chúng ta cần tuân thủ thôi. Còn onConfigurationChanged() được gọi khi Activity được reset lại (hủy và tạo lại ngay bởi hệ thống), và chúng ta cũng code bên trong phương thức này theo yêu cầu của Navigation Drawer luôn, với mong muốn được cập nhật trạng thái của nó. onPostCreate() thì mình sẽ không bao giờ nói đến, còn với onConfigurationChange() thì mình sẽ có bài viết nói rõ hơn về nó nhé.

  4. Cho mình hỏi, sao mình copy các đoạn code vào sau đó Run nó trên máy áo thì cứ thông báo “Tournote has stop” không mở ứng dụng được…

    1. Bạn có thể xem logcat để biết báo lỗi thế nào nhé. Bạn có thể copy lỗi đó lên đây. Nhưng mình đoán có thể MainActivity của bạn không kế thừa từ AppCompatActivity mà kế thừa từ Activity luôn, nên các phương thức getSupportActionBar() gây ra crash.

      1. Cảm ơn bạn đã phản hồi. Mỗi khi thêm thẻ này vào thì “run” chương trình nó báo 1 dọc lỗi mình nhìn vào không hiểu:
        07-18 20:37:51.803 12491-12491/vn.test.test E/AndroidRuntime: FATAL EXCEPTION: main
        Process: vn.test.test, PID: 12491
        java.lang.RuntimeException: Unable to start activity ComponentInfo{vn.test.test/vn.test.test.MainActivity}: android.view.InflateException: Binary XML file line #38: Binary XML file line #38: Error inflating class android.support.design.widget.NavigationView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
        Caused by: android.view.InflateException: Binary XML file line #38: Binary XML file line #38: Error inflating class android.support.design.widget.NavigationView
        Caused by: android.view.InflateException: Binary XML file line #38: Error inflating class android.support.design.widget.NavigationView
        Caused by: java.lang.ClassNotFoundException: Didn’t find class “android.support.design.widget.NavigationView” on path: DexPathList[[zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/base.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_dependencies_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_resources_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_0_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_1_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_2_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_3_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_4_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_5_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_6_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_7_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_8_apk.apk”, zip file “/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_slice_9_apk.apk”],nativeLibraryDirectories=[/data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/lib/x86, /system/lib, /vendor/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at android.view.LayoutInflater.createView(LayoutInflater.java:606)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:790)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:287)
        at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:139)
        at vn.test.test.MainActivity.onCreate(MainActivity.java:24)
        at android.app.Activity.performCreate(Activity.java:7009)
        at android.app.Activity.performCreate(Activity.java:7000)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        07-18 20:37:51.805 12491-12491/vn.test.test E/AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
        Suppressed: java.io.IOException: No original dex files found for dex location /data/app/vn.test.test-cgz4lg_A-s04KwjAyUYyoQ==/split_lib_resources_apk.apk
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:353)
        at dalvik.system.DexFile.(DexFile.java:100)
        at dalvik.system.DexFile.(DexFile.java:74)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:374)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:337)
        at dalvik.system.DexPathList.(DexPathList.java:157)
        at dalvik.system.BaseDexClassLoader.(BaseDexClassLoader.java:65)
        at dalvik.system.PathClassLoader.(PathClassLoader.java:64)
        at com.android.internal.os.ClassLoaderFactory.createClassLoader(ClassLoaderFactory.java:73)
        at com.android.internal.os.ClassLoaderFactory.createClassLoader(ClassLoaderFactory.java:88)
        at android.app.ApplicationLoaders.getClassLoader(ApplicationLoaders.java:69)
        at android.app.ApplicationLoaders.getClassLoader(ApplicationLoaders.java:35)
        at android.app.LoadedApk.createOrUpdateClassLoaderLocked(LoadedApk.java:693)
        at android.app.LoadedApk.getClassLoader(LoadedApk.java:727)
        at android.app.LoadedApk.getResources(LoadedApk.java:954)
        at android.app.ContextImpl.createAppContext(ContextImpl.java:2270)
        at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5639)
        at android.app.ActivityThread.-wrap1(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1656)
        … 6 more

Gửi phản hồi