Giap Hiep

I'm Giap Hiep

I'm a web developer, a gymer. I enjoy share something i know that help people's work!
Giap Hiep

Mẹo nhỏ khi thực thi màn hình Splash sử dụng Android Navigation

Trong sự kiện Google I/O 2018, Google đã công bố một số thư viện hỗ trợ cho các lập trình viên Android. Một trong số đó là Navigation Architecture Component. Mình sẽ không trình bày những điều cơ bản của thành phần này vì nó đã có sẵn trong tài liệu dành cho các lập trình viên rồi. Hãy chắc chắn đọc chúng trước khi đi xa hơn nhé!
Khi Google công bố về Navigation, mình đã ngay lập tức thử sử dụng nó. Và cũng ngay lập tức mình gặp một vấn đề.

Vấn đề:

Giống như hầu hết các ứng dụng, ứng dụng của tôi cũng có một màn hình Splash. Nếu màn hình này là activity thì sẽ rất dễ dàng để remove màn này khỏi back stack bằng cách sử dụng hàm finish() và biến cờ Intent.FLAG_ACTIVITY_NEW_TASK, Intent.FLAG_ACTIVITY_CLEAR_TOP. Nhưng khi sử dụng Navigation component thì mỗi màn hình là một fragment. Và chúng ta không thể dùng cách đó được. Hãy xem minh họa dưới đây để hiểu rõ hơn về vấn đề này.

Đầu tiên, mình sẽ tạo một file Navigation graph với hai màn hình là SplashFragment và HomeFragment.

<?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"
    app:startDestination="@id/splashFragment">

    <fragment
        android:id="@+id/splashFragment"
        android:name="com.example.navigationsample.fragments.SplashFragment"
        android:label="SplashFragment"
        tools:layout="@layout/layout_splash">
        <action
            android:id="@+id/action_splashFragment_to_homeFragment"
            app:destination="@id/homeFragment"/>
    </fragment>
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.navigationsample.fragments.HomeFragment"
        android:label="HomeFragment"
        tools:layout="@layout/layout_home">
    </fragment>
</navigation>

Trong màn SplashFragment, mình sẽ sử dụng Navigation như sau:

findNavController().navigate(R.id.action_splashFragment_to_homeFragment)

Xem kết quả sau khi run app nhé!

Nếu đúng theo follow thì sau khi màn SplashFragment chạy xong sẽ chuyển sang màn HomeFragment và màn SplashFragment phải bị hủy đi. Nhưng trong trường hợp này, màn SplashFragment vẫn còn tồn tại.

Giải pháp

Thư viện Navigation cung cấp̣ lớp NavOptions. Nơi mà chúng ta có thể chỉ định những tùy chọn mở rộng khi điều hướng. Chúng ta có thể sử dụng setPopUpTo để giải quyết vấn đề này.
Code:

public NavOptions.Builder setPopUpTo (int destinationId, 
 boolean inclusive)

Trong đó:

  • destinationId: Kiểu int. Là id của màn hình cần hiện lên(ở bài này là SplashFragment). Và clear tất cả các màn trên đường đi từ màn SplashFragment đến màn HomeFragment.
  • inclusive: Kiểu boolean. Là giá trị quyết định có clear màn hình có id = destinationId hay không. Trong bài này thì ta cần clear cả màn SplashFragment nên ta sẽ truyền vào param thứ 2 là true.

Chúng ta sẽ cập nhật code màn SplashFragment như sau:

findNavController()
        .navigate(R.id.action_splashFragment_to_homeFragment,
                null,
                NavOptions.Builder()
                        .setPopUpTo(R.id.splashFragment,
                                true).build()
        )

Run app và ta được kết quả như sau:

Bonus

Vậy là đã giải quyết được vấn đề mà mình đã chỉ ra ở trên.
Ngoài ra, thay vì sử dụng code java thì chúng ta cũng có thể giải quyết vấn đề trong bài bằng cách sử dụng hai thuộc tính dưới đây trong file Navigation graph:

app:popUpTo="@id/splashFragment"            
app:popUpToInclusive="true"

Chúng ta update lại file xml Navigation graph như sau:

<?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"
    app:startDestination="@id/splashFragment">

    <fragment
        android:id="@+id/splashFragment"
        android:name="com.example.navigationsample.fragments.SplashFragment"
        android:label="SplashFragment"
        tools:layout="@layout/layout_splash">
        <action
            android:id="@+id/action_splashFragment_to_homeFragment"
            app:destination="@id/homeFragment"
            app:popUpTo="@id/splashFragment"
            app:popUpToInclusive="true"/>
    </fragment>
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.navigationsample.fragments.HomeFragment"
        android:label="HomeFragment"
        tools:layout="@layout/layout_home">
    </fragment>
</navigation>

Cảm ơn bạn đã quan tâm!

Nguồn: https://proandroiddev.com/android-navigation-component-tips-tricks-implementing-splash-screen-f0f5ce046a09