Thông Thạo Jetpack – Phần 4 – Navigation (Tập 2)

Posted by
Rating: 5.0/5. From 2 votes.
Please wait...

Chào mừng các bạn đến với chủ đề về Android Jetpack.

Các bạn hãy xem trước các kiến thức sơ bộ về Navigation – Tập 1 trước khi đọc qua bài hôm nay nhé.

Ở bài hôm trước chúng ta đã cùng nhau tìm hiểu xem Navigation là gì và được cấu thành từ các thành phần nào. Chúng ta còn làm quen với ứng dụng mẫu được xây dựng lên từ thư viện này nữa đấy.

Phần tiếp theo hôm nay chúng ta sẽ cùng nhau xây dựng ứng dụng ở các bước ban đầu, song song với việc làm rõ nghĩa dần các khái niệm đã được đưa ra từ phần trước, giúp bạn hiểu rõ hơn về thư viện Navigation này.

Trước tiên chúng ta hãy cùng xem lại kịch bản của ứng dụng mà chúng ta sẽ xây dựng.

Kịch bản của ứng dụng chúng ta sắp xây dựng

Tạo Mới Project

Giờ thì bạn hãy tạo mới một project, rồi đặt tên NavigationSample, hoặc bất cứ tên nào mà bạn thích nhé. Project này sẽ được viết bằng ngôn ngữ Kotlin.

Tạo mới NavigationSample project
Tạo mới NavigationSample project

Khai Báo Thư Viện

Đầu tiên nhất, trước khi làm việc gì, chúng ta phải khai báo sử dụng thư viện Navigation vào build.gradle của module. Việc làm này sẽ giúp cho hệ thống down về các thư viện cần thiết cho chúng ta có thể đến các bước tiếp theo.

//...

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.5.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    // Navigation
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

Xây Dựng Navigation Graph

Bạn đã được làm quen với Navigation Graph ở phần trước rồi, phần này mình không nói lại khái niệm nữa mà tiến hành xây dựng thể loại resource mới này.

Tạo Mới Một Resource Navigation Graph

Trong cửa sổ Project của Android Studio, bạn hãy click chuột phải vào thư mục res/ của project rồi chọn New > Android Resource File.

Tạo mới Android Resource File
Tạo mới Android Resource File

Sau đó, một cửa sổ khai báo hiện ra. Bạn hãy định nghĩa một cái tên cho resource Navigation Graph này. Mình đặt là login_nav_graph (vì Navigation Graph này định nghĩa luồng Login, bạn có thể sẽ phải cần nhiều Navigation Graph khác cho từng chức năng khác bên trong ứng dụng của bạn sau này, nên chúng ta sẽ tập đặt tên có phân biệt chức năng ngay bây giờ luôn, bằng cách thêm tiền tố login_ vào resource). Ở mục Resource type trong cửa sổ này, bạn hãy tìm và chọn Navigation, đây là kiểu resource chúng ta muốn tạo. Sau đó hãy để mọi thứ khác như mặc định rồi nhấn OK.

Khai báo cho resource Navigation Graph
Khai báo cho resource Navigation Graph

Khi này bạn sẽ thấy một thư mục mới, và một file XML mới được thêm vào project của chúng ta.

Resource Navigation mới được thêm vào project
Resource Navigation mới được thêm vào project

Làm Quen Với Resource Navigation Graph

Sau khi hoàn thành bước tạo mới resource Navigation Graph trên đây, bạn cũng sẽ thấy Android Studio mở resource này lên editor. Nếu không thấy bạn có thể kích đúp lên file login_nav_graph.xml để mở nó. Hiện tại editor đang hiển thị nội dung file này của chúng ta với một giao diện trống trơn. Mình sẽ mượn hình ảnh của Google để giới thiệu về việc thao tác trên editor đối với resource mới này như sau.

Cái này gọi là Navigation Editor
Cái này gọi là Navigation Editor

Editor dùng để hiển thị resource Navigation Graph chúng ta gọi với cái tên ngắn gọn hơn là Navigation Editor. Navigation Editor trên đây có đánh số, chia nó làm 3 phần riêng biệt giúp chúng ta dễ dàng thao tác hơn.

  1. Destination panel: là một thanh chứ đựng các destination. Destination là gì thì mình có nói sơ qua ở phần trước rồi. Các destination bên trong Destination panel được chia ra thành hai nhóm, HOSTGRAPH. Giao diện của mỗi nhóm này sẽ hơi khác với Android Studio của chúng ta một xíu, nhưng nó vẫn là một. Còn ý nghĩa của chúng là gì thì lát nữa trong quá trình tạo các destination chúng ta sẽ cùng tìm hiểu nhé. Nhưng dù cho các destination được chia nhóm như thế nào đi chăng nữa thì chúng cũng sẽ được vẽ vào bên trong vùng thứ 2 được nói đến tiếp theo đây.
  2. Graph Editor: đây có thể nói là tinh túy của Navigation Graph, một nơi giúp vẽ ra sự tương quan giữa các thành phần bên trong Navigation Graph này. Nên nhớ là editor dành cho resource này cũng có 3 tab giúp chúng ta dễ dàng chuyển đổi giữa việc xem cấu trúc của file theo dạng Code, dạng Design hay dạng hỗn hợp Split nhé .
  3. Attributes: nơi chứa đựng các tham số định nghĩa cho từng thành phần được chọn bên trong Graph Editor. Cụ thể các tham số này sẽ được mình nói rõ hơn khi tạo các thành phần tương ứng.

Thêm NavHost Vào Activity

Cho đến giờ phút này thì Navigation Editor của chúng ta chưa có thể hiện thông tin gì cả. Bởi vì login_nav_graph cũng chỉ mới là một resource mới được tạo ra, chưa có nơi nào “nhận chứa chấp” em nó, và em nó cũng chưa nhận hiển thị gì lên cả.

Thêm NavHostFragment Vào MainActivity

Bước tiếp theo này chúng ta sẽ tìm “nơi chứa chấp” em resource login_nav_graph. Nơi chứa chấp này đã được mình giới thiệu ở phần trước với cái tên NavHost. Không nói nhiều vì phần trước nói hết rồi, tóm lại thì MainActivity chính là nơi chứa chấp em login_nav_graph. Vậy chúng ta hãy mở activity_main.xml lên, đảm bảo editor đang hiện ở tab Design. Bạn hãy xóa TextView có sẵn khi chúng ta tạo mới project này đi. Đảm bảo giao diện của MainActivity khi này đang chỉ có một ConstraintLayout thôi nhé.

Sau đó hãy tìm trên thanh Palette, vào nhóm Containers, bạn sẽ thấy một thành phần UI có tên là NavHostFragment. NavHostFragment chính là “nơi chứa chấp” Navigation Graph mà chúng ta muốn tìm.

Tất cả những thao tác nãy giờ được biểu diễn bằng một hình ảnh như dưới đây.

Đi tìm NavHostFragment chuẩn bị đưa vào MainActivity
Đi tìm NavHostFragment chuẩn bị đưa vào MainActivity

Khi tìm thấy NavHostFragment rồi thì bạn hãy tiến hành kéo thả thành phần này vào trong màn hình thiết kế (nếu có lỡ ngu ngơ chỗ này quá thì bạn có thể xem thêm bên phần xây dựng giao diện cho ConstraintLayout nhé).

Ngay khi vừa kéo thả thành phần này vào giao diện thiết kế, bạn sẽ thấy một cửa sổ lập tức hiện ra, hỏi bạn rằng NavHost này sẽ chứa Navigation Graph nào. Dĩ nhiên là login_nav_graph mà chúng ta đã tạo trên kia rồi. Hãy chọn nó và nhấn OK nhé.

Cửa sổ hỏi chúng ta Navigation Graph nào sẽ được thêm vào NavHost này
Cửa sổ hỏi chúng ta Navigation Graph nào sẽ được thêm vào NavHost này

Canh Chỉnh NavHostFragment

Sau đó, bởi vì chúng ta cần MainActivity hiển thị từng Fragment con của nó toàn màn hình, nên chúng ta cũng sẽ điều chỉnh NavHostFragment sao cho chúng cũng sẽ chiếm hết không gian dài rộng của MainActivity (điều này cũng gợi ra cho các bạn một ý tưởng, rằng NavHostFragment thực ra cũng giống như các layout khác mà thôi, bạn có thể thoải mái xếp đặt chúng ở bất cứ vị trí nào và bất cứ kích thước nào trên màn hình tùy thích). Với bài học của chúng ta hôm nay thì NavHostFragment được biểu diễn như sau.

Canh chỉnh lại kích thước của NavHost
Canh chỉnh lại kích thước của NavHost

Hiểu Hơn Về NavHostFragment

Nếu bạn tò mò muốn biết nhiều hơn về NavHostFragment. Bạn có thể xem các thuộc tính được liệt kê ở editor trên đây. Nhưng mình lại muốn bạn nắm nhiều hơn thông qua code XML của MainActivity. Bạn hãy chuyển sang tab Code của activity_main nhé.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/login_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

Cũng không có gì cao siêu, nhưng mình cũng sẽ nói rõ ra cho các bạn nắm cách mà MainActivity chứa đựng một NavHostFragment như thế nào.

  • androidx.fragment.app.FragmentContainerView: đây là một layout mới, được thiết kế ra để chứa đựng các Fragment.
  • android:name: định nghĩa rằng layout trên đây đang chứa thành phần NavHostFragment. Nó giống như khi bạn khai báo fragment tĩnh vào trong một layout như bên bài viết về Fragment vậy.
  • android:navGraph: thuộc tính giúp hệ thống biết NavHostFragment này chứa đựng login_nav_graph.
  • app:defaultNavHost: thuộc tính này giúp hệ thống chặn nút system back cho chúng ta. System back là nút back của thiết bị. Bởi vì NavHostFragment của chúng ta sẽ chứa đựng rất nhiều destination trong đó, cũng giống như một trang Web chứa nhiều page con vậy, một khi người dùng đã đi qua các page, hay các destination, rồi nhấn nút back của thiết bị, chúng ta mong muốn các destination trước đó sẽ được hiển thị trở lại. Khi đó cờ của thuộc tính này là true sẽ giúp ứng dụng của chúng ta phản ứng theo kịch bản như vậy. Còn nếu bạn khai báo là false, khi nhấn back của thiết bị ở bất cứ destination con nào, cả một NavHost cũng sẽ trở về trạng thái trước đó (có thể khiến ứng dụng bị đóng luôn).

Thêm Destination Vào Navigation Graph

Lúc bấy giờ nếu quay lại tab Design của Navigation Editor đang hiển thị login_nav_graph. Bạn sẽ thấy thông tin trong Destination panel thể hiện rằng activity_main chính là Host (là nơi chứa chấp) login_nav_graph này.

Navigation Graph đã có một chút thông tin
Navigation Graph đã có một chút thông tin

Bước tiếp theo đây chúng ta sẽ xây dựng một kịch bản các destination ngay trên Navigation Graph này. Nếu như hướng dẫn trên editor, bạn hãy tìm và click lên icon có hình . Bạn sẽ thấy một popup sau xuất hiện.

Chức năng thêm vào một destination
Chức năng thêm vào một destination

Đây là nơi mà chúng ta lựa chọn các destination nào sẽ được thêm vào. Và vì do chúng ta không có bất kỳ một Fragment hay destination nào sẵn có trong project hết nên popup này mới trống trơn như vậy. Bạn có thể chọn Create new destination để được dẫn đến các chọn lựa tạo một destination mong muốn. Hoặc bạn có thể chọn placeholder để tạo trước một destination giữ chỗ, rồi sau đó sẽ khai báo destination cụ thể cho placeholder này sau. Nhưng với mình thì mình nghĩ bạn nên đóng popup này lại, chúng ta chỉ làm quen với nó thôi. Chúng ta sẽ lần lượt tạo các Fragment cần thiết, rồi sẽ quay lại popup này, khi đó mọi chuyện sẽ dễ dàng hơn rất nhiều nhé.

Tạo Các Fragment

Như đã nói, bước này chúng ta sẽ tạo các Fragment. Theo kịch bản chúng ta sẽ cần đến HomeFragment, ProfileFragment, SignInFragment, SignUpFragment.

Để đỡ dài dòng, bạn hãy tham khảo link này cho việc tạo mới một Fragment, dựa vào đó bạn hãy tạo một Blank Fragment, tức là một Fragment gọn nhất có thể nhé. Đây là màn hình khi mình tạo một HomeFragment.

Tạo mới HomeFragment
Tạo mới HomeFragment

Dù cho source code của HomeFragment có rườm rà khi được tạo mới như thế nào, chúng ta cũng hãy làm cho nó thật gọn như sau.

class HomeFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_home, container, false)
    }
}

Giao diện cho HomeFragment thì cũng nên xây dựng ở mức vừa đủ với 1 Button như sau. Lưu ý là các bạn có thể thoải mái xây dựng giao diện theo cách các bạn biết, không nhất thiết phải là ConstraintLayout như mình. Chúng ta đang nói về Navigation chứ không nói sâu về việc tạo giao diện nhé.

Giao diện cho HomeFragment
Giao diện cho HomeFragment

Tương tự, bạn hãy tạo ProfileFragment và giao diện của nó. Với một TextView chào hỏi và một Button logout.

Giao diện cho ProfileFrgament
Giao diện cho ProfileFrgament

SignInFragment và giao diện của nó. Các EditText dùng cho việc nhập liệu username, password. Một Button sign in và một Button sign up.

Giao diện cho SignInFragment
Giao diện cho SignInFragment

Và cuối cùng là SignUpFragment và giao diện của nó. EditText dùng để khai báo username và password, và Button để tạo tài khoản.

Giao diện cho SignUpFragment
Giao diện cho SignUpFragment

Hi vọng các bạn đừng nản chí. Vì dù sao chúng ta đang xây dựng cả một ứng dụng hoàn chỉnh cơ mà. Đây có thể được xem là prototype, một bản dựng ban đầu của ứng dụng.

Nhưng vì sao mình muốn các bạn xây dựng trước giao diện cho các Fragment này. Đơn giản vì Navigation Editor rất tuyệt, nó không những có thể giúp hiển thị một bản đồ các destination, mà còn hiển thị trước giao diện thiết kế của destination đó nữa. Một lát bạn sẽ thấy sự hiệu quả từ bước này của chúng ta.

Nếu bạn có gặp khó khăn gì trong việc xây dựng Fragment và giao diện của chúng ở bước này. Bạn hoàn toàn có thể xem link đến source code trên Github mình để ở cuối bài đấy nhé.

Thêm Các Fragment Vào Navigation Graph

Đến bây giờ thì chúng ta hoàn toàn có thể thêm các Fragment này vào Navigation Graph, chúng sẽ là các destination. Bạn hãy mở lại login_nav_graph.

Giờ thì bạn hãy quay lại nhấn vào icon . Bạn đã thấy danh sách các Fragment nay đã được liệt kê lên chưa nào, có cả tên file giao diện, kèm với hình thiết kế giao diện của Fragment đó nữa. Thật tuyệt.

Thêm vào một destination nay đã có đầy đủ các Fragment
Thêm vào một destination nay đã có đầy đủ các Fragment

Ngoại trừ placeholder ra, bạn cứ lần lượt chọn từng Fragment trong danh sách trên, nó sẽ thêm vào trong Navigation Graph cho bạn. Bạn hãy thoải mái sắp xếp lại vị trí bằng cách kéo chuột sao cho chúng nhìn khá là đẹp mắt như sau.

Navigation Graph đã có đầy đủ destination
Navigation Graph đã có đầy đủ destination

Tuy chúng ta có bao nhiêu đây Fragment thôi, khá dễ quản lý, nhưng nếu tương lai ứng dụng của chúng ta có nhiều Fragment và vô tình chúng ta thêm lộn một Fragment nào đó vào Navigation Graph này, thì không sao, bạn cứ click chuột phải vào bất kỳ destination nào bên trong Navigation Graph và chọn Delete là xong. Điều này sẽ giúp chúng ta gỡ destination ra khỏi Navigation Graph thôi, không xóa luôn file giao diện của Fragment đâu bạn nhé.

Thêm một điều nữa mình muốn các bạn để ý trong Navigation Graph này. Đó là luôn có một destination sẽ được đánh dấu với biểu tượng hình ngôi nhà, chỗ tiêu đề của nó. Mặc định destination được thêm vào đầu tiên sẽ được đánh dấu với biểu tượng này. Trong trường hợp này chính là homeFragment.

homeFragment được đánh dấu có hình ngôi nhà
homeFragment được đánh dấu có hình ngôi nhà

Bạn hoàn toàn có thể chỉ định destination khác sẽ là ngôi nhà, bằng cách chọn click một destination trong Navigation Editor rồi nhấn vào icon ở phía trên editor này. Việc đánh dấu với icon này trên một destination, là nhằm chỉ định rằng đây chính là start destination. Một start destination sẽ là destination đầu tiên được nhìn thấy khi ứng dụng của chúng ta được mở lên, và cũng sẽ là destination cuối cùng mà người dùng sẽ nhìn thấy trước khi ứng dụng kết thúc (khi người dùng nhấn lần lượt các nút back).

Tìm Hiểu Thuộc Tính Của Destination Bên Trong Navigation Graph

Cũng với login_nav_graph đang mở, và tab Design của editor này đang chọn. Bạn hãy nhấn vào bất kỳ một destination nào, bạn sẽ thấy một số thuộc tính của nó bên khung Attributes.

Thuộc tính của homeFragment
Thuộc tính của homeFragment

Hiện tại ở bước này bạn chỉ mới quan tâm vài thuộc tính sau của từng destination.

  • id: hiển nhiên là id của destination rồi. Khi đến bước di chuyển qua lại giữa các destination, bạn sẽ thấy id này được sử dụng như thế nào nhé.
  • label: là tên của destination. Tên này được dùng như với trường hợp hiển thị tên của màn hình lên Toolbar chẳng hạn. Chúng ta sẽ tìm hiểu vấn đề này ở bài sau, còn bây giờ bạn cứ để như mặc định nhé.
  • name: chính là Fragment mà destination này đang đại diện cho.

Gờ thì bạn hãy chuyển sang tab Code của editor. Bạn cũng sẽ thấy các destination khi này thực chất chính là các Fragment được khai báo bên trong thành phần cha navigation.

<?xml version="1.0" encoding="utf-8"?>
<navigation 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/login_nav_graph"
    app:startDestination="@id/homeFragment">
    <fragment
        android:id="@+id/profileFragment"
        android:name="com.yellowcode.navigationsample.ProfileFragment"
        android:label="fragment_profile"
        tools:layout="@layout/fragment_profile" />
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.yellowcode.navigationsample.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
    <fragment
        android:id="@+id/signInFragment"
        android:name="com.yellowcode.navigationsample.SignInFragment"
        android:label="fragment_sign_in"
        tools:layout="@layout/fragment_sign_in" />
    <fragment
        android:id="@+id/signUpFragment"
        android:name="com.yellowcode.navigationsample.SignUpFragment"
        android:label="fragment_sign_up"
        tools:layout="@layout/fragment_sign_up" />
</navigation>
  • app:startDestination: thuộc tính này nằm ở navigation, giúp hệ thống biết được destination nào chính là start destination. Chính là destination có icon hình ngôi nhà mà bạn đã thấy bên tab Design trên kia.
  • tools:layout: sẽ chỉ định layout của Fragment được dùng hiển thị lên ở dạng xem trước bên tab Design. Nếu bạn thay đổi layout nào khác cho bất kỳ destination nào ở bên đây, bạn sẽ thấy sự khác biệt bên tab Design. Tuy nhiên mình không khuyến khích bạn sửa nhé, chỉ vọc chơi nếu thích rồi trả lại như cũ thôi.
  • Các thuộc tính còn lại như id, name, label thì mình dã nói ở trên rồi.

Ứng dụng đã dần thành hình. Và bạn cũng đã hiểu hơn nhiều về việc sử dụng Navigation này rồi. Nếu bây giờ bạn thực thi ứng dụng, thì chỉ màn hình nào được đánh dấu là start destination mới được nhìn thấy, và hoàn toàn chưa có sự tương tác gì cho ứng dụng cả. Chúng ta sẽ cùng xây dựng tiếp ứng dụng này ở phần tiếp theo nhé.

Kết Luận

Như vậy là chúng ta đã làm quen và hiểu tốt về hai trong ba thành phần chính của Navigation đã được nói ở phần trước, đó chính là Navigation GraphNavHost. Chúng ta sẽ tiếp tục nói về NavController ở bài sau, và vận dụng tất cả các thành phần này để cùng nhau xây dựng một ứng dụng hoàn chỉnh nhé.

Download Source Code Mẫu

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

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao ở mỗi bài viết nếu thấy thích.
– Comment bên dưới mỗi bài viết nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
– Ủng hộ blog theo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.

Leave a Reply