1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NavigationとSafeArgsを既存プロジェクトに適用する

Posted at

はじめに

既存のプロジェクトでは、複数のアクティビティによる画面遷移の構成となっておりましたが、Androidアプリとしてデファクトスタンダードな構成とするため

  • Single Activity, Multiple Fragments化
  • Navigationによる画面遷移
  • SafeArgsによるパラメータの引き渡し

を適用しました。
なので、この記事では上記の適用方法について紹介していきたいと思います。

プロジェクトの設定

NavigationとSafeArgsのために、
Projectのapp/build.gradleに以下を設定

buildscript {    
    dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3"
    }
}

appのbuild.gradleに以下を設定

apply plugin: 'androidx.navigation.safeargs.kotlin'
apply plugin: 'kotlin-parcelize'

dependencies {
    implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
    implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
}

Single Activity, Multiple Fragments化

ActivityをFragmentに変換する

これまでActivityとして作成したクラスはAppCompatActivityの継承からFragmentの継承に変更する修正が基本になります。
以下のようにアクションバーにタイトルの設定や戻るボタンを設定している場合は以下のように変換します。

変換前のActivity

class LicenseActivity : AppCompatActivity() {
    @SuppressLint("ResourceType")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_license)
        // タイトルの設定
        title = getString(R.string.license)
        // 戻るボタンの設定
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
    }
}

変換後のFragment

class LicenseFragment : Fragment() {
    /** ライセンス画面のビュー */
    private var rootView: View? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Fragmentのメニューを有効にする
        setHasOptionsMenu(true)
    }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // タイトルの設定
        requireActivity().title = getString(R.string.license)
        // 戻るボタンの設定
        (requireActivity() as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }
}

Single Activityを用意

Fragment全体を管理するActivityを以下のように1つ用意します。

class PasswordMemoActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setTheme(R.style.AppTheme)
        setContentView(R.layout.activity_password_memo)
    }
}

xmlファイルの中で、後述するNavigationのファイルを設定します。

<?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:id="@+id/loginView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.highcom.passwordmemo.PasswordMemoActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/passwordmemo_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Navigation Editorで画面遷移を設定

nav_graphの設定

res/navigation/nav_graph.xmlを作成して、以下のようにDesignタブでグラフィカルに画面遷移を設定します。
nav_graph.png

  • passwordListFragmentinputPasswordFragment
  • inputPasswordFragmentpasswordListFragment

の遷移についてxmlファイルの設定は以下となります。

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

    <fragment
        android:id="@+id/passwordListFragment"
        android:name="com.highcom.passwordmemo.ui.fragment.PasswordListFragment"
        android:label="fragment_password_list"
        tools:layout="@layout/fragment_password_list" >
        <action
            android:id="@+id/action_passwordListFragment_to_inputPasswordFragment"
            app:destination="@id/inputPasswordFragment"
            app:popUpTo="@id/passwordListFragment"
            app:enterAnim="@anim/slide_in_left"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_right"
            app:popExitAnim="@anim/slide_out_right" >
            <argument
                android:name="editData"
                app:argType="com.highcom.passwordmemo.ui.PasswordEditData" />
        </action>
    </fragment>
    <fragment
        android:id="@+id/inputPasswordFragment"
        android:name="com.highcom.passwordmemo.ui.fragment.InputPasswordFragment"
        android:label="InputPasswordFragment"
        tools:layout="@layout/fragment_input_password" >
        <action
            android:id="@+id/action_inputPasswordFragment_to_passwordListFragment"
            app:destination="@id/passwordListFragment"
            app:popUpTo="@id/passwordListFragment"
            app:popUpToInclusive="true"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_right"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_left" />
        <argument
            android:name="editData"
            app:argType="com.highcom.passwordmemo.ui.PasswordEditData" />
    </fragment>
</navigation>

Animationの設定

nav_graph.xml
            app:enterAnim="@anim/slide_in_left"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_right"
            app:popExitAnim="@anim/slide_out_right" 

上記の設定に対するAnimationファイルを設定します。
res/animフォルダ配下に以下のファイルを設定。

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

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromXDelta="100%"
        android:toXDelta="0%" />
</set>
slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromXDelta="0%"
        android:toXDelta="-100%" />
</set>
slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromXDelta="-100%"
        android:toXDelta="0%" />
</set>
slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="@android:integer/config_shortAnimTime"
        android:fromXDelta="0%"
        android:toXDelta="100%" />
</set>

この設定をすることで、Navigationによる画面遷移時に、左から右にスライドするような画面遷移の挙動になります。

SafeArgsによるパラメータの引き渡し

NavigationにSafeArgsを設定

nav_graph.xml
            <argument
                android:name="editData"
                app:argType="com.highcom.passwordmemo.ui.PasswordEditData" />

actionタグの中にargumentを設定する事でデータの受け渡しが実現できます。
app:argTypeに設定するのは基本的にはプリミティブ型なのですが、データクラスのような値を渡したい場合には、以下のようなParcelableを継承したデータクラスを作成します。

package com.highcom.passwordmemo.ui

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class PasswordEditData(
    val edit: Boolean = false,
    val id: Long = 0,
    val title: String = "",
    val account: String = "",
    val password: String = "",
    val url: String = "",
    val groupId: Long = 1,
    val memo: String = "",
    val inputDate: String = "",
) : Parcelable

Navigationを利用した画面遷移の呼び出し

    val passwordEditData = PasswordEditData()
    findNavController().navigate(PasswordListFragmentDirections.actionPasswordListFragmentToInputPasswordFragment(editData = passwordEditData))

上記のようにfindNavController().navigateで必要な画面遷移のアクションを呼び出して、引数にSafeArgsで設定したデータクラスの値を設定して呼び出す事で画面遷移が実現できます。
また、受け取る際には以下のような実装になります。

    /** Navigationで渡された引数 */
    private val args: InputPasswordFragmentArgs by navArgs()
    val passwordEditData = args.editData

さいごに

対応について以上となります。
全体のコードについては、以下のリポジトリで公開しておりますので、参考になれば幸いです。

1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?