HIRO Tracks

山根正大です。Fintech業界エンジニアが日々学んだ知識を発信します。

【Android】画面遷移とクラス設計について

これはなに?

  • Androidで画面遷移するための知識整理・Tips
    • IntentとActivityによる起動のみです
    • 公式サイトもある程度参照してます
    • Fragmentとかの話はまた別途...
  • こちらの本の第7章「画面遷移とIntentクラス」の内容を自分なりに整理した内容となります。
  • (+α) 起動ロジックは呼び出される側においた方が良くね?という話

目次

Androidの画面遷移は"遷移"ではない

  • WebアプリとかだとURLによるルーティングやリダイレクトがあって別画面に「遷移する」けど、Androidだとそうでは無い
  • 元画面の上に別画面が生成されて「載る」形になる。
  • 元画面に戻る際は、「載っている画面を消す」形になる。

新画面作るのに必要なもの・手順

layout用xml

  • 画面構成を定義する
    • AndroidStudioでビジュアライズ可能
  • さまざまな部品を組み合わせる
    • レイアウト構造の定義:LineraLayout,ConstraintLayout...
    • その他部品:TextView,Button,ListView,etc...
  • 一例として、以下は単純なリストを表示するだけのxml

activity_new.xml

<?xml version="1.0" encoding="utf-8"?>
<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/simpleList"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Activityクラス

  • 画面を呼び出すにあたって様々な処理を書くクラス

アクティビティの概要  |  Android デベロッパー  |  Android Developers

アクティビティにより、アプリが UI を描画するウィンドウが用意されます。通常、このウィンドウは画面全体に表示されますが、画面よりも小さく、他のウィンドウの上に重なることもあります。

NewActivity.kt

import androidx.appcompat.app.AppCompatActivity

class NewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_new) //作成済みのxmlを呼び出す

        // 以下詳細処理記載...

AndroidManifest.xml

  • Activityクラスを作ったら、AndroidManifest.xmlに追加が必要。
    • 追加しないと起動時エラー
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sample.mysample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.IntentSample">
        <!--★★ここに新規アクティビティを追加-->
        <activity android:name=".NewActivity"></activity> 
        <!--遷移元アクティビティ-->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Intent

一般的なインテント  |  Android デベロッパー  |  Android Developers

インテントを使用すると、実行したい簡単なアクション(「地図を表示する」、「写真を撮る」など)を Intent オブジェクトに記述することによって、別のアプリでアクティビティを開始できます。

インテントとインテント フィルタ  |  Android デベロッパー  |  Android Developers

Activity はアプリ内の 1 つの画面を表しています。Activity の新しいインスタンスを開始するには、Intent を startActivity() に渡します。Intent には、開始するアクティビティが記述され、必要なデータがすべて含まれています。

  • startActivity(intent: Intent)を実行することで、別画面を表示することが可能
  • 指定方法には明示的なものと暗黙的なものがある
  • Intent自体はActivity以外(Serviceとか)も起動する
val newIntent = Intent(this@MainActivity, NewActivity::class.java)
startActivity(newIntent)
//これ以降NewActivityにより画面遷移開始

起動ロジックは呼び出される側に閉じた方がいいと思う

Activityの起動処理は起動されるActivityで実装すべし - Qiita

上記記事を読んで「確かに」と思ったのでメモ。クラス設計的な話。

教則本に載ってるパターン

  • 画面遷移先固有のロジックを「呼び出す側」クラスに書いている
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 略

        // 画面遷移先固有のロジックを「呼び出す側」クラスに書いている
        if (画面遷移トリガーがあったら) {
          val extra = "NewActivity起動に必要な情報"
          val newIntent = Intent(this@MainActivity, NewActivity::class.java)
          newIntent.putExtra("extra", extra)
          startActivity(newIntent)
        }
    }
class NewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_new)
        }
    }

実務だとこうした方が便利だと思う

  • 画面遷移先固有のロジックを「呼び出される側」クラスに書いている
    • companionObject(javaでいうstatic)使って、クラス外から利用できるようにしておく
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 略

        // 呼び出すだけ
        if (画面遷移トリガーがあったら) {
            NewActivity.start(this@MainActivity, "NewActivity起動に必要な情報")
        }
    }
}
class NewActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_new)
        // 略
    }

    // 呼び出される側で自身の起動ロジックを持っておく
    companion object {
        private const val EXTRA_KEY = "EXTRA_KEY"

        fun start(context: Context, extraValue: String) {
            context.startActivity(
                Intent(context, NewActivity::class.java).also {
                    it.putExtra(EXTRA_KEY, extraKey)
                }
            )
        }
    }
}
メリット
  • startActivty時の固有ロジックを呼び出し側で意識しなくて良くなる
    • 必要な情報を渡してstartするだけでOK
  • 例だと引数をそのまま渡すだけだから簡単だが、引数が多くなったり文字列を加工したりする必要が出てきた場合、こちらの方が幸せなはず。