1
3

More than 3 years have passed since last update.

【Android】Navigation Safe Args でリソースファイルに指定された値をデフォルト値として渡したい

Last updated at Posted at 2020-11-12

最近、Android Jetpack のNavigationを使っているのですが、Fragment 間の画面遷移が簡単に実現できて便利だなと感じています。
ですが、ハマりポイントもありましたので備忘録として残しておきます。

Safe Args でリソースファイルに指定された値をデフォルト値として渡したい

Safe Argsとは、ナビゲーションやデータの受け渡しをする際、型の安全性を保証してくれる仕組みです。
strings.xmlに定義した文字列をデフォルト値として渡したいとします。

strings.xml
<string name="message">テストメッセージ</string>

フラグメントなどのレイアウト、ナビゲーション グラフをそれぞれ下記のように実装します。
各コードの説明はここではしません。
Navigation の説明については下記の記事などがわかりやすかったです。

FirstFragment.kt
class FirstFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_first, container, false)
        view.button.setOnClickListener {
            val action = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
            it.findNavController().navigate(action)
        }
        return view
    }
}
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.FirstFragment">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        android:id="@+id/button"/>

</FrameLayout>
SecondFragment.kt
class SecondFragment : Fragment() {
    private val args: SecondFragmentArgs by navArgs()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_second, container, false)
        view.text_view.text = args.message
        return view
    }
}
fragment_second.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.SecondFragment">

    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
nav_graph.xml
<?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/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.example.view.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first">
        <action
            android:id="@+id/action_firstFragment_to_secondFragment"
            app:destination="@id/secondFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.example.view.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second">
        <argument
            android:name="message"
            android:defaultValue="@string/message"
            app:argType="string" />
    </fragment>
</navigation>
activity_main.xml
<?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/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" />

</androidx.constraintlayout.widget.ConstraintLayout>

ですが、アプリ起動時に以下のようなログを出力してクラッシュします。

2020-11-11 22:14:44.768 9509-9509/com.example E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example, PID: 9509
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MainActivity}: android.view.InflateException: Binary XML file line #14 in com.example:layout/activity_main: Binary XML file line #14 in com.example:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: android.view.InflateException: Binary XML file line #14 in com.example:layout/activity_main: Binary XML file line #14 in com.example:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
     Caused by: android.view.InflateException: Binary XML file line #14 in com.example:layout/activity_main: Error inflating class androidx.fragment.app.FragmentContainerView
     Caused by: java.lang.RuntimeException: Exception inflating com.example:navigation/nav_graph line 22
        at androidx.navigation.NavInflater.inflate(NavInflater.java:90)
        at androidx.navigation.NavController.setGraph(NavController.java:499)
        at androidx.navigation.NavController.setGraph(NavController.java:481)
        at androidx.navigation.fragment.NavHostFragment.onCreate(NavHostFragment.java:237)
        at androidx.fragment.app.Fragment.performCreate(Fragment.java:2685)
        at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:280)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2236)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2009)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1965)
        at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1830)
        at androidx.fragment.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:303)
        at androidx.fragment.app.FragmentContainerView.<init>(FragmentContainerView.java:166)
        at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:51)
        at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
        at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:356)
        at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:335)
        at android.view.LayoutInflater.tryCreateView(LayoutInflater.java:1069)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:997)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:481)
        at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:696)
2020-11-11 22:14:44.771 9509-9509/com.example E/AndroidRuntime:     at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:170)
        at com.example.MainActivity.onCreate(MainActivity.kt:13)
        at android.app.Activity.performCreate(Activity.java:7802)
        at android.app.Activity.performCreate(Activity.java:7791)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: org.xmlpull.v1.XmlPullParserException: unsupported value 'テストメッセージ' for string. You must use a "reference" type to reference other resources.
        at androidx.navigation.NavInflater.inflateArgument(NavInflater.java:206)
        at androidx.navigation.NavInflater.inflateArgumentForDestination(NavInflater.java:146)
        at androidx.navigation.NavInflater.inflate(NavInflater.java:121)
        at androidx.navigation.NavInflater.inflate(NavInflater.java:132)
        at androidx.navigation.NavInflater.inflate(NavInflater.java:81)
            ... 42 more

原因

app:argType="string"に対して、android:defaultValue="@string/message"となっており、データ型が一致していないのが原因です。

Caused by: org.xmlpull.v1.XmlPullParserException: unsupported value 'テストメッセージ' for string. You must use a "reference" type to reference other resources.

一見すると良さそうに見えるのですが、@string/messageは String そのものではなくリソース ID(int)なので、データ型が異なると見なされるようです。

解決策

nav_graph.xmlapp:argTypereferenceに変更します。

nav_graph.xml
...
<argument
    android:name="message"
    android:defaultValue="@string/message"
    app:argType="reference" />
...

そうするとリソース ID を渡すよう変更されるので、SecondFragmentを下記のように変更します。

SecondFragment.kt
...
view.text_view.text = getString(args.message)
...

これで起動時にクラッシュしなくなり、正常に実行できるようになります。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3