【Android】画面回転時における値の保存について

Android, Kotlin

はじめに

NX開発推進部Androidアプリ開発グループ所属の深沢と申します。普段はAndroidアプリの開発業務に携わっています。

スマホを使用していると、ふとした時にスマホが傾いてしまい、画面の向きが変わってしまうことがあるかと思います。その時に下のgifのようにアプリの内容がリセットされたらとても不便だと思いませんか?

Androidアプリはとある処理を追加しないと、画面回転時に値が保持されないようになっています。
ここでは、その値保持の処理についてまとめました。KotlinやAndroid Studioの基本的な使い方は知っている前提で話を進めています。

今回使用するコード

今回使用するコードは以下のとおりです。ボタンを押すとサイコロの出目が変わるというシンプルなものになっています。

上記コードを実行しますと、画面を回転したときに出目の値が1にリセットされてしまいます。

これから、画面を回転させた時でも内容をリセットさせないようにする方法を2つほど紹介致します。

方法1: 値を保存して、アクティビティの再作成時に取得する

実装手順

以下の5ステップで実装していきます。

  1. ログの確認
  2. 値の保存
  3. 値の取得
  4. 保存した値に対応する出目の画像を表示させる
  5. 値の保存を維持する

ステップ1: ログの確認(省略可)

画面を回転させたときのライフサイクルに関するログを確認します。

ログを確認すると、 onDestroyでアクティビティが破棄された後に、 onCreate()で再び作成されていることがわかります。

ステップ2. 値の保存

値を保持する処理を書いていきます。

1. override fun onSaveInstanceState(outState: Bundle)MainActivity内に追加します。

onSaveInstanceState()は状態の保存を行う関数です。この関数のログも追加して、画面を回転させると onSaveInstanceState()はアクティビティが停止した後に呼び出されることがわかります。この関数はアプリがバックグラウンドに移行するたびに呼び出されます。

2. Bundleというデータを保管してくれるクラスに値を保存する時に、キーを使用するので設定します。
キーをクラス宣言の前に const valとして設定します。今回は KEY_DICEとしました。

3.値を保存させる処理を onSaveInstanceState()内に書いていきます。

ステップ3: 値の取得

ステップ2で保存した値を onCreate()で取得します。

1.再作成時は onCreate()から走るため、 onCreate()内に if (savedInstanceState != null)と書きます。

これで Bundle内に値があるときのみコードを走らせるようにしました。

2.1のif文の中に以下を追記します。

保存したときと同じキーを用いて、 savedValueに値を代入します。しかし、このままだと未定義エラーが出てしまうため、修正していきます。

3.クラス宣言直下に保存されている値を入れる変数を定義します。値は初期状態の値である 1としました。

これで未定義エラーは消えました。

ステップ4: 保存した値に対応する出目の画像を表示させる

値を保存して、変数に格納することができたため、この変数を用いて画面に画像を表示させます。

1. MainActivity内に displaySavedDice(savedValue: Int)という関数を作成し、引数に savedValueを取ります。

2. displaySavedDice()内に rollDice()と同じ要領で、 savedValueの出目に対応した画像を表示させる処理を書きます。

3.ステップ3で作成した savedValueの下に displaySavedDice(savedValue)を追加します。

これで、アクティビティを破棄した際にも値が保存されるようになりました。

4.アプリを実行し、確認します。回転させても出目が保存されるようになりました。

しかし、もう一度回転させると、出目が1に変更されてしまいます。

ステップ5: 値の保存を維持する

出目が戻る原因を確認します。ステップ1のログより画面を回転させるとActivityが再作成されるため、 onCreate()が再び呼ばれることがわかります。

上から順に処理していき、 if (savedInstanceState != null)...に到着します。ここで savedValueにはアクティビティ破棄前に保存されていた値が代入されるのですが、 diceRollには代入されません。そのため、 diceRollの値は初期値の 1となります。

このまま再び画面を回転させるとどうなるでしょうか?コードは fun onSaveInstanceState(outState: Bundle)に到達します。ここで diceRollの値を保存するのですが、現在 diceRollには 1が入っているため、出目は何であれ 1が保存されてしまうことがわかるかと思います。

そこで、 saveValueと同じところで diceRollの値も更新する必要があります。

1. onCreate()のif文内で diceRollの値を更新します。

2.再び実行し、画面を複数回回転させても出目が保持されていることを確認します。

完成コード

完成した MainActivity.ktは以下のとおりです。( activity_main.xmlは変更していないため割愛しています)

方法2: 画面回転時にアクティビティを破棄させない

方法1では「アクティビティを再作成したときに、破棄前と同じ状態になるように処理する」という考え方でしたが、方法2では「画面回転時にそもそもアクティビティを破棄しないようにする」という考えのもと作業を行います。

実装方法

こちらは AndroidManifest.xmlandroid:configChanges="orientation|screenSize"という一文を追加するだけです。

実行すると、方法1と同様に画面を回転させても値が変更されません。

ログを確認してみます。

画面を回転させてもアクティビティが破棄されていないことが確認できます。

この android:configChangesを設定した場合、画面回転時に onConfigurationChangedがコールバックされます。そのため、このメソッドをオーバーライドすると、画面回転時の処理を記述することができます。

以下のように書くと画面回転時にトーストメッセージが表示されるようになります。

まとめ

以上が画面回転時の値の保持についての方法でした。今回まとめたもの以外にも方法は複数あると思いますので、興味がある方は調べてみてください。

最後まで読んでいただきありがとうございました!

参考

Android, Kotlin