画面の向きを変えたりとか

  • 投稿日:
  • by
  • カテゴリ:

image

端末の向きを変えれば、アプリも回転したりしなかったりします。回転させないのであれば、特に気にすることはなく、終了。

だけど、回転させたい場合には、それなりの対応をしなければなりません。フォームのデザインを、縦用と横用とを用意するのはもちろん、最低限必要なことです。アプリケーションによっては、それで対応完了する場合もあります。

厄介なのはそれで終わらない場合......内部データの保持が必要な場合なのです。

画面の回転が起こると、Activity は一旦破棄され、然る後に、新しい向きに合わせてActivityが生成され、アプリの実行が再開されます。

Activityは、アプリケーションの本体といってもいい存在ですので、これが破棄されると、内部データも全て破棄されてしまいます。ストップウォッチならば、動作中の状態、ラップタイムのログなどといったデータが全て失われてしまいます。

これを防ぐ方法は色々あるのですが、一応、推奨されている方法は、onSaveInstanceState()と、onRestoreInstanceState()を@Overrideして、必要な情報を、Bundleに保存しBundleから復帰させることです。

単純なデータ型の保存・復帰は Bundle.putInt(i) とか i = Bundle.getInt()とかすればいいので、問題は無いのですが、ややこしいのは、ListViewのデータのようなものたちです。

こういったデータは、SerializableやParcelable でなければなりません。リストであるなら、ParcelableなArrayListである必要があります。Budle は、getParcelableArrayList()/putParcelableArrayList()という機能を持っているからです。

がさがさと、このあたりの情報をググって漁ってくると、Bundle.putSerializable(key, (Serializable)list); list = (List<T>)Bundle.getSerializable(key);でOKみたいなことが書いてあって、画面の回転だけだったら確かにそれで動くのですが、Eclipseは、検証もしないでList<T>型にキャストして良いのかよ?とか警告出しているし、実際に、動作中に、HOMEボタンを押すなどして、ActivityがonPause()を起こすと、Pacelの例外を出して死んでしまいます。

これを避けるためには、ListはArrayListに、要素クラスであるTは、Parcelableにしてやる必要があります。クラスをParcelableにするのは、簡単です。

public class LaptimeListItem implements Parcelable {
    public int lap;
    public long time;
    public long elapsed;
   
    public LaptimeListItem()
    {
        lap = 0;
        time = 0;
        elapsed = 0;
    }
    private LaptimeListItem(Parcel source)
    {
        lap = source.readInt();
        time = source.readLong();
        elapsed = source.readLong();
    }
   
    public static final Parcelable.Creator<LaptimeListItem> CREATOR = new Parcelable.Creator<LaptimeListItem>() {

        @Override
        public LaptimeListItem createFromParcel(Parcel source) {
            // TODO 自動生成されたメソッド・スタブ
            return new LaptimeListItem(source);
        }

        @Override
        public LaptimeListItem[] newArray(int size) {
            // TODO 自動生成されたメソッド・スタブ
            return new LaptimeListItem[size];
        }
       
    };
    @Override
    public int describeContents() {
        // TODO 自動生成されたメソッド・スタブ
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // TODO 自動生成されたメソッド・スタブ
        dest.writeInt(lap);
        dest.writeLong(time);
        dest.writeLong(elapsed);
    }

}

これは、実際に、ラップタイムを保持しているクラスですが、implements Parcelable を宣言に追加して、describeContents()とwriteToParcel()のうち、writeToParcel()の方で、保存すべきメンバをParcelに詰め込みます。

これだけでは、不十分で、public static final Parcelable.Creator<T>() CREATOR というメンバを作成し、createFromParcel()とnewArray()で、それぞれ、new T(source)とnew T[size] を返すようにします。(自動生成されたのは return null なので、必ず変更します。)

さらに、createFromParcel()が必要とするので、コンストラクタ T(Parcel source)を作り、その中で、writeToParcel()で詰め込んだ順で、Parcelから取り出して、クラスのメンバにセットします。

ここまで作ってやっておけば、Bundle.putParcelableArrayList(key, list)とlist = Bundle.getParcelableArrayList(key)で、データの受け渡しを、クラッシュすることなく行うことができます。もちろん、Eclipseも文句を言わなくなります。大したことしていないアプリでも、これだけ面倒な処理が必要なのです。とほほ。