# 【Android】Navigationで戻ってきたことを検知する方法【Navigation】
# この記事でやること
AndroidのNavigationでFragmentの画面遷移を作ったときに、戻ってきたことを検知する方法が初見ではわからなかったので、ここにメモします。
↓ やりたいことのイメージはこんな感じになります。
FirstFragment → SecondFragmentのときは「はじめまして。」と表示され、SecondFragment ← ThirdFragmentのときは「ただいま。」と表示されるように、戻ってきたかどうかを判定して文言を変えています。
# 環境
以下の環境で動かしています。
- Android Studio 4.0
- Navigation 2.3.0
# ざっくり言うと
結論から言うと「popBackStack()
を使うな。 戻る際にも navigate()
を使え。」です。
戻る際に単に this.findNavController().popBackStack()
で戻るのではなく、戻るとき用の遷移アクションを定義します。
そうすると、戻る際に前のFragmentを再生成させ、そのときに新たに引数を与えてやることができます。
# とりまコード見て
NavGraphのコードはこんな感じです。
my_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/my_nav_graph"
app:startDestination="@id/firstFragment">
<fragment
android:id="@+id/firstFragment"
android:name="net.aridai.myapp.FirstFragment"
android:label="FirstFragment"
tools:layout="@layout/first_fragment">
<argument
android:name="saishonoparam"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="net.aridai.myapp.SecondFragment"
android:label="SecondFragment"
tools:layout="@layout/second_fragment">
<argument
android:name="modottekitaka"
android:defaultValue="false"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_secondFragment_to_thirdFragment"
app:destination="@id/thirdFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
<action
android:id="@+id/action_secondFragment_to_firstFragment"
app:destination="@id/firstFragment"
app:enterAnim="@anim/nav_default_pop_enter_anim"
app:exitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@layout/first_fragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/thirdFragment"
android:name="net.aridai.myapp.ThirdFragment"
android:label="ThirdFragment"
tools:layout="@layout/third_fragment">
<action
android:id="@+id/action_thirdFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@anim/nav_default_pop_enter_anim"
app:exitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@layout/second_fragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
SecondFragmentの定義の中で modottekitaka
(戻ってきたか) という引数を定義しています。
<argument
android:name="modottekitaka"
android:defaultValue="false"
app:argType="boolean"
app:nullable="false" />
FirstFragmentからSecondFragmentに遷移するコードはこうです。
(FirstFragment.kt
)
this.button1.setOnClickListener {
val directions = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
this.findNavController().navigate(directions)
}
上の modottekitaka
引数の定義から分かるように、デフォルトの false
の値を与えています。
ThirdFragmentからSecondFragmentに戻るコードはこうです。
(ThirdFragment.kt
)
this.button1.setOnClickListener {
val directions = ThirdFragmentDirections.actionThirdFragmentToSecondFragment(modottekitaka = true)
this.findNavController().navigate(directions)
}
modottekitaka = true
を与えて this.findNavController().navigate(directions)
で遷移しています。
this.findNavController().popBackStack()
じゃあないですよ。
そんでSecondFragmentでは SecondFragmentArgs#modottekitaka
を見て文言を変えてます。
// (省略)
private val args: SecondFragmentArgs by this.navArgs()
// (省略)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.text.text = when (this.args.modottekitaka) {
true -> "「ただいま。」"
false -> "「はじめまして。」"
}
// (省略)
}
以上です。
# 他の部分のコードも置いておくよ
# MainActivityのコード
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<fragment
android:id="@+id/navHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="false"
tools:ignore="FragmentTagUsage" />
</LinearLayout>
MainActivity.kt
package net.aridai.myapp
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
internal class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContentView(R.layout.main_activity)
val args = FirstFragmentArgs(saishonoparam = "お腹すいたなぁ〜")
this.findNavController(R.id.navHostFragment).setGraph(R.navigation.my_nav_graph, args.toBundle())
}
}
# 1つ目のFragmentのコード
first_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="FirstFragment"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="テキスト"
android:textColor="#000000"
android:textSize="18sp"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="→"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
FirstFragment.kt
package net.aridai.myapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.first_fragment.*
internal class FirstFragment : Fragment() {
private val args: FirstFragmentArgs by this.navArgs()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.first_fragment, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.text.text = this.args.saishonoparam
this.button1.setOnClickListener {
val directions = FirstFragmentDirections.actionFirstFragmentToSecondFragment()
this.findNavController().navigate(directions)
}
}
}
# 2つ目のFragmentのコード
second_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="SecondFragment"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="テキスト"
android:textColor="#000000"
android:textSize="18sp"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="→"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="←"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
SecondFragment.kt
package net.aridai.myapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.second_fragment.*
internal class SecondFragment : Fragment() {
private val args: SecondFragmentArgs by this.navArgs()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.second_fragment, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.text.text = when (this.args.modottekitaka) {
true -> "「ただいま。」"
false -> "「はじめまして。」"
}
this.button1.setOnClickListener {
val directions = SecondFragmentDirections.actionSecondFragmentToThirdFragment()
this.findNavController().navigate(directions)
}
this.button2.setOnClickListener {
val directions = SecondFragmentDirections.actionSecondFragmentToFirstFragment(saishonoparam = "唐揚げ食べたくない?")
this.findNavController().navigate(directions)
}
}
}
# 3つ目のFragmentのコード
third_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="ThirdFragment"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="←"
android:textColor="#000000"
android:textSize="20sp"
tools:ignore="HardcodedText" />
</LinearLayout>
ThirdFragment.kt
package net.aridai.myapp
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.third_fragment.*
internal class ThirdFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.third_fragment, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
this.button1.setOnClickListener {
val directions = ThirdFragmentDirections.actionThirdFragmentToSecondFragment(modottekitaka = true)
this.findNavController().navigate(directions)
}
}
}