Navigation component & Using safe args

1. Introduction

Navigation Architecture Component đơn giản hoá điều hướng trong ứng dụng của bạn, giúp bạn hình dung được luồng chạy của ứng dụng một cách trực quan nhất. Thư viện này cung cấp một số chức năng như:

  • Tự động xử lý fragment transactions
  • Xử lý animations and transitions
  • Xử lý Deep linking
  • Triển khai các navigation UI patterns (như drawers and bottom nav) đơn giản hơn.
  • Type safety (Chuyển dữ liệu giữa các màn hình an toàn hơn)
  • Cung cấp công cụ edit create flow trực quan.

2. What you’ll build

Ở bài viết này mình chỉ đề cập đến các chức năng gồm :
– Cách tạo và sử dụng Navigation Component cơ bản
– Sử dụng Type safety trong việc truyền dữ liệu.

3. Build

B1. cReate project

– Project này mình sẽ sử dụng Kotlin để demo, các bạn vẫn tạo project như bình thường và tick vào kotlin lúc khởi tạo.

– Thêm thư viện navigation vào build.gradle.

 def nav_version = "2.1.0-rc01"
 implementation 'androidx.core:core-ktx:1.0.2'
 implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
 implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

Về cơ chế navigation component chỉ sử dụng 1 activity_main và các màn hình con sẽ là các fragment. Để tạo flow và sử dụng chúng ta sẽ nhúng một NavHostFragment vào activity_main.xml bằng 2 step sau:

Step 1: Tạo nav_graph.xml
Click nào res/Android Resource File và tạo như hình dưới với type navigation.

Step 2: Nhúng nav_graph vào activity_main.xml

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph" />

Bây giờ cơ bản là chúng ta đã config xong Navigation với Host là activity_main.

b2. tạo các màn hình con và flow.

Bây giờ chúng ta sẽ quay lại nav_graph.xml để tạo giao diện và flow.

Ở đây mình tạo 1 flow đơn giản như trên, về cách tạo qua 2 bước sau.

Click Create new distination để tạo 1 fragment với, trường hợp đã có sẵn fragment rồi thì bạn có thể thêm code ở phần Text.
Tạo các fragment cần thiết như sau.

Tiếp theo để tạo các action chuyển đổi màn hình, chỉ cần click chuột và kéo vào các màn hình muốn mở, android studio sẽ tự động tạo ra code.

 <fragment
        android:id="@+id/splashFragment"
        android:name="com.android.navigation_component_example.SplashFragment"
        android:label="Splash"
        tools:layout="@layout/fragment_splash" >
        <action
            android:id="@+id/action_splash_to_login"
            app:destination="@id/loginFragment"
            app:popUpTo="@+id/splashFragment"
            app:popUpToInclusive="true"/>
    </fragment>
  • action_splash_to_login : Khi gọi hàm này sẽ mở màn hình LoginFragment
  • Thông thường thì Splash sẽ mất đi bạn sẽ cần phải xoá nó khỏi stack bằng cách thêm 2 dòng :
app:popUpTo="@+id/splashFragment"
app:popUpToInclusive="true"

Giờ thì gọi hàm sử dụng thôi 😀

class SplashFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_splash, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Handler().postDelayed({
            Navigation.findNavController(view).navigate(R.id.action_splash_to_login)
        }, 2000)
    }
}

Tương tự tạo tiếp các màn hình Login/Main/Detail các button để mở màn hình như flow hoàn chỉnh ở hình đầu.

B3. sử dụng type safety

Để sử dụng thì chúng ta cần thêm các thư viện phụ thuộc vào build.gradle top.

 def nav_version = "2.1.0-rc01"
 classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"

Mở tiếp build.gradle app thêm.

apply plugin: "androidx.navigation.safeargs.kotlin"
compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }

Lưu ý tích hợp JAVA-8 vào nhé (ahihi đồ ngốc =)) tiến hành Sync Project

Plugin này sẽ auto genarate code khi chúng ta update ở nav_graph.xml công việc chỉ cần click Sync mỗi khi thay đổi là đc. (omg xịn xò dị sao =)) )

Tiếp theo mình sẽ demo chuyển dữ liệu 2 loại.
– Basic Types
– Object

HomeFragment mình sẽ yêu cầu tham số nhận được có tên myArg kiểu bất kỳ, bằng cách thêm argument như bên dưới sau đó bấm Sync.

<fragment
        android:id="@+id/loginFragment"
        android:name="com.android.navigation_component_example.LoginFragment"
        android:label="Login"
        tools:layout="@layout/fragment_login" >
        <action
            android:id="@+id/action_login_to_home"
            app:destination="@id/homeFragment"
            app:popUpTo="@+id/loginFragment"
            app:popUpToInclusive="true"/>
    </fragment>
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.android.navigation_component_example.HomeFragment"
        android:label="Home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_home_to_detail"
            app:destination="@id/detailFragment" />
        <argument
            android:name="myArg"
            android:defaultValue="0" />
    </fragment>

Sau đó bấm Sync để genarate ra code.

Tiếp theo chúng ta viết code để từ LoginFragment mở HomeFragment và truyền thêm giá trị vào.

btnLogin.setOnClickListener {
            if (edtInputValue.text.toString().isNotEmpty()) {
                val value = edtInputValue.text.toString().toInt()
                val action = LoginFragmentDirections.actionLoginToHome(value)
                it.findNavController().navigate(action)
            }
        }

LoginFragmentDirections.java có được khi Sync Project

Ở HomeFragment lấy giá trị bằng cách gọi.

val safeArgs: HomeFragmentArgs by navArgs()
val value = safeArgs.myArg

HomeFragmentArgs.java có được khi Sync Project

Cơ bản thư viện Safe sẽ sử dụng code ở nav_graph.xml để genarate ra các file cấu trúc sẵn, việc cần làm chỉ gọi ra để dùng thôi 😀

Okay ở trên là truyền dữ liệu kiểu basic, vậy mình muốn truyền 1 object thì như thế nào ??

Đầu tiên mình tạo 1 Model: User.kt

data class User(var userName: String) : Serializable 

Ở Trên mình đã từ Login –> Home rồi.
Giờ mình sẽ tạo 1 cái từ Home –> Detail và truyền User.kt từ Home sang.

Sau khi tạo Model User.kt rồi thì mình sẽ thêm Fragment mới là DetailFragment.kt và đưa nó vào nav_graph.xml. Ở đây mình sẽ yêu cầu tham số truyền vào là 1 Object User.

<fragment
        android:id="@+id/detailFragment"
        android:name="com.android.navigation_component_example.DetailFragment"
        android:label="Detail"
        tools:layout="@layout/fragment_detail">
        <argument
            android:name="userArg"
            app:argType="com.android.navigation_component_example.model.User"
            app:nullable="true"
            android:defaultValue="@null" />

    </fragment>

Các bạn lưu ý ở đây app:argType phải đúng đường dẫn tới model luôn nhé.

Các kiểu dữ liệu hỗ trợ.

Oke bước tiếp theo cũng bấm Sync để genarate ra code.

Cách sử dụng cũng tương tự kiểu basic type thôi.
HomeFragment.kt truyền đi 
Nhận ở DetailFragment.kt.

btnOpenDetail.setOnClickListener {
            if (edtInputValue.text.toString().isNotEmpty()) {
                val user = User(edtInputValue.text.toString())
                val action = HomeFragmentDirections.actionHomeToDetail(user)
                it.findNavController().navigate(action)
            }
        }
 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val safeArgs : DetailFragmentArgs by navArgs()
        val user : User = safeArgs.userArg as User
        tvReceiveValue.text = "Value Receive:\n" + user.toString()
    }

Yep, Cơm ngon canh ngọt thì build là chạy luôn nha =)))
Vậy ở bài viết này mình đã giới thiệu sơ qua cho các bạn về cách tạo và sử dụng Navigation Component rồi.
Còn khá nhiều thứ hay ho như animation, deeplink, trong quá trình làm việc chúng ta có thể tìm hiểu thêm.
Về cơ bản đây là một thư viện vô cùng tuyệt vời.


Github tham khảo.

Nguyễn Linh

Chia sẻ để cùng tiến bộ...