※この記事はpotatotips45の発表をまとめたものです。
はじめに
ずっとiOSアプリの開発をしていましたが、ここ最近Androidのアプリの開発をはじめました。
iOSアプリとAndroidアプリでは仕組みや実装方法は違いますが、似たような機能や動きをするのでiOSで培った経験や考え方を活かしながら実装しています。
しかし最近、iOSアプリにはないUpボタンの実装についてとても悩みました。
今回はそのUpボタンについて調べたことや実装方法についての紹介をしたいと思います。
AndroidのUpボタンとは?
UpボタンはAndroidのナビゲーションに関するものです。
iOSでいうところのNavigationBarのBackボタンやエッジスワイプにあたる機能になります。
AndroidのナビゲーションはUpボタンの他にBackボタンも存在します。
BackボタンはiOSのNavigationBarのBackボタン、エッジスワイプと同じで前の画面に戻るナビゲーションです。
Upボタンは、Backボタンとは違いただ前に戻るだけのナビゲーションではありません。
Upボタンは階層的な画面遷移をするためのナビゲーションです。
階層的な画面遷移について
階層的な画面遷移とは、どこから来たかによって戻る画面が変わることです。
以下はGoogleのPlay Storeアプリの遷移例です。
1 ホームからの遷移
以下の用にアプリのホーム画面からアプリ詳細画面に遷移した際、Upボタンを押すとホーム画面に戻ります。
2 リストを挟んでの遷移
1とは違い、アプリのホーム画面からリスト画面(アプリの新着リストなど)を一旦挟みアプリ詳細に遷移するとリスト画面に戻ります。
実装について
Upボタンの実装については、Android Developerに実装についての参考があります。
以下がサンプルコードです。
<application ... >
    ...
    <activity
        android:name="com.example.myfirstapp.MainActivity" ...>
        ...
    </activity>
    <activity
        android:name="com.example.myfirstapp.DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName="com.example.myfirstapp.MainActivity" >
        <!-- Parent activity meta-data to support 4.0 and lower -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>
</application>
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
    when (item?.itemId) {
        android.R.id.home -> {
            val upIntent = NavUtils.getParentActivityIntent(this)
            if (NavUtils.shouldUpRecreateTask(this, upIntent!!)) {
                // 戻る先のタスクがないので生成する
                TaskStackBuilder.create(this)
                        .addNextIntentWithParentStack(upIntent)
                        .startActivities()
            } else {
                NavUtils.navigateUpTo(this, upIntent)
            }
            return true
        }
    }
    return super.onOptionsItemSelected(item)
}
AndroidManifest.xmlにPARENT_ACTIVITYを設定しています。
Upボタンを押して画面を戻るときに、NavUtils.getParentActivityIntent(this)で指定したPARENT_ACTIVITYを取得して画面遷移をしています。
実装としてはすごくシンプルだと思います。
しかし、この実装方法ではPARENT_ACTIVITYが固定されてしまうので戻る画面が静的になってしまいます。
戻る画面が動的に変わる実装方法
Play Storeのような動的に戻る画面遷移を実現するために、以下のように実装しました。
実装方法
- 戻りたい画面の
Intentを生成する - navigateUpToを使用する
 - 
IntentにFLAG_ACTIVITY_CLEAR_TOPを設定する 
コード
class ChildActivity : AppCompatActivity(), ItemDetailEventHandler {
    private lateinit var fromActivity: FromActivity
    private const val FROM_ACTIVITY = "from_activity"
    enum class FromActivity {
        HOME,
        SEARCH_RESULT,
        ・・・
    }
    companion object {
        private const val FROM_ACTIVITY = "from_activity"
        fun start(context: Context, fromActivity: FromActivity) {
            context.startActivity(
                    Intent(context, ChildActivity::class.java).apply {
                        putExtra(FROM_ACTIVITY, fromActivity)
                    })
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        fromActivity = intent.getSerializableExtra(FROM_ACTIVITY) as FromActivity 
    }
    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
        when (item?.itemId) {
            android.R.id.home -> {
                val intent = createIntent(fromActivity)
                intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
                navigateUpTo(intent)
                return true
            }
            else -> return false
        }
    }
    private fun createIntent(fromActivity: FromActivity): Intent {
        return when (fromActivity) {
            FromActivity.HOME -> Intent(this, HomeActivity::class.java)
            FromActivity.SEARCH_RESULT -> Intent(this, SearchItemResultActivity::class.java)
            ・・・
        }
    }
}
class HomeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ChildActivity.start(context, ItemDetailActivity.FromActivity.ITEM_RANKING)
    }
}
戻る画面が動的になるため、AndroidManifest.xmlにPARENT_ACTIVITYを設定せず、Intentを生成するようにしました。
また、普段は画面遷移をする際はstartActivityを使用しますが、今回はnavigateUpToを使用します。
navigateUpToはIntentで指定されたアクティビティに移動し、アクティビティを閉じるメソッドです。
動的に戻る画面遷移を実装する上で一番重要なのはIntentのflagsにFLAG_ACTIVITY_CLEAR_TOPを設定することです。
FLAG_ACTIVITY_CLEAR_TOPを設定をすることで、画面が閉じられるような見た目になります。
これを設定しないで実行すると、新しく画面が作られたような見た目になってしまいます。
まとめ
- UpボタンはAndroidのナビゲーションに関するボタン
 - 同じナビゲーションに関するボタンのBackボタンとは違い、階層的な画面遷移をする
 - 戻る画面が変わる画面遷移をするには
Intentを自前で生成する必要がある 
以上がUpボタンについてのまとめです。
階層的な画面遷移を実装して、よりよいアプリにしていきたいですね!
