Weather app: 使用Navigation管理畫面流程

Table of Contents

Table of Contents


上一篇文章介紹使用Glide來顯示各種天氣圖示。 本篇文章將介紹使用Navigation管理畫面流程。


Github

https://github.com/scobin/Android_WeatherApp/commits/feature/Navigation


build.gradle中入Navigation

開啟 app 資料夾下的buid.gradle檔案,加入以下程式碼。 完成後執行「sync now」。

// navigation
def nav_version = "2.1.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

使用Navigation來控制畫面流程時,需要掌握以下三項東西。 透過這三項東西來設計畫面移動,移動對象,以及移動處理,下面將分別介紹各項的內容。

  • Navigation Graph
  • NavHost
  • NavController

一個能讓畫面變化流程設計的xml檔案。一般會產生在 res/navigation下。 跟畫面佈局文件一樣,可以切換 Text/Design模式來設計。 這裡製作檔名為「nav_graph」的Navigation Graph檔案,並將各個Fragment放上。

nav_graph

「Text」模式的內容如下。

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

    <fragment
        android:id="@+id/areaListFragment"
        android:name="bruntho.com.tennki.ui.areaList.AreaListFragment"
        android:label="area_list_fragment"
        tools:layout="@layout/area_list_fragment" >
        <action
            android:id="@+id/action_areaListFragment_to_weatherDetailFragment"
            app:destination="@id/weatherDetailFragment" />
    </fragment>
    <fragment
        android:id="@+id/aboutFragment"
        android:name="bruntho.com.tennki.ui.about.AboutFragment"
        android:label="about_fragment"
        tools:layout="@layout/about_fragment" />
    <fragment
        android:id="@+id/weatherDetailFragment"
        android:name="bruntho.com.tennki.ui.weatherDetail.WeatherDetailFragment"
        android:label="WeatherDetailFragment" />
</navigation>

可以使用 action標籤來定義了畫面移動。 destination表示目標畫面,因此這裡所示的內容表示可由areaListFragment移動到weatherDetailFragment。

<action
    android:id="@+id/action_areaListFragment_to_weatherDetailFragment"
    app:destination="@id/weatherDetailFragment" />

表示目標畫面的顯示位置。 本例來說,各個Fragment畫面都是在MainActivity中裡的FrameLayout中被呈現的,因此在FrameLayout中設定一個NavHost。 修改MainActivity的佈局檔案(activity_main.xml)如下。

...
<FrameLayout
    android:paddingBottom="56dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"
        />
</FrameLayout>
...

注意 name, navGraph的部分要設置正確。


設置好Navigation Graph與NavHost後,就可以在程式碼中處理畫面變更。 在想要做畫面變更的時候使用以下方式即可。

// 想要傳遞給目標畫面的資料
val bundle = bundleOf(
    "cityId" to cityId
    "cityName" to cityName
)
// 變換至目標畫面
Navigation.findNavController().navigate(R.id.action_weatherListFragment_to_weatherDetailFragment, bundle)

透過 **findNavController()**取得NavController後,使用navigate函數並設定相關參數。 這裡第一個參數是在nav_graph中定義好的action id。 第二個參數是要傳送給目標畫面的Bundle型態資料。


BottomNavigationView, FloatingActionButton

這裡設計了BottomNavigationView,讓BottomNavigationView來做Area list與About畫面的切換。 並且加上FloatingActionButton來製作增加按鈕。

修改activity_main.xml如下。

<FrameLayout
...
</FrameLayout>

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:id="@+id/bottom_coor"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottom_app_bar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:layout_gravity="bottom"
        app:fabAlignmentMode="center"
        android:backgroundTint="@color/bottom_app_bar"
        app:contentInsetLeft="0dp"
        app:contentInsetStart="0dp">
        
        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/transparent"
            app:itemIconTint="#ffffff"
            app:itemTextColor="#ffffff"
            android:id="@+id/bottom_nav"
            app:menu="@menu/bottom_nav_menu"/>
    </com.google.android.material.bottomappbar.BottomAppBar>
    <com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/fab"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onClick="showCityList"
                android:src="@drawable/ic_add_24dp"
                app:layout_anchor="@id/bottom_app_bar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

因為要使用BottomAppBar來切換畫面,所以製作bottom_nav_menu.xml並設定到BottomNavigationView的menu上。 特別注意這裡的item id要跟nav_graph中的fragment id一致。

?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/areaListFragment"
        android:title="Area"
        android:icon="@drawable/ic_playlist_add_24dp"/>
    <item android:id="@+id/aboutFragment"
        android:title="About"
        android:icon="@drawable/ic_info_outline_24dp"/>
</menu>

然後在MainActivity中將NavController設定給BottomNavigationView,如此BottomNavigationView就可以控制畫面切換了。

navController = Navigation.findNavController(this,R.id.nav_host_fragment)
bottom_nav.setupWithNavController(navController)