2011年1月7日金曜日

Intent で Parcelable を実装した自作オブジェクトの受け渡しをしてみた

Activity から別の Activity を起動する際に、
自作クラスのオブジェクトの受け渡しについてサンプル・アプリを作ってみました。

Intent のメソッド一覧を見ていると、
putExtra(String name, <何か>) で、Intent にいろんな値をセットできるようになっています。

それを呼び出された側の Activity では、get<何たら>Extra(String name) で
値を取得できるようになっています。

この中に、putExtra(String name, Parcelable value) と getParcelableExtra(String name) があります。

Parcelable を自作クラスに適用すれば Activity 間で、オブジェクトの受け渡しができるだろう、
ということで、作ってみたサンプル・アプリが以下になります。

アクティビティ間でオブジェクトの受け渡しを行うということで、
メインとサブの2つのアクティビティを用意しました。

メイン・アクティビティ (IntentParcelableTestActivity) にはボタンを1個配置しています。

このボタンを押すと、サブ・アクティビティ (SubActivity) を起動するようになっており、
このとき、Pacelable インタフェースを実装した自作クラス (ParcelableTest) のオブジェクトを渡すようにしました。

    メイン・アクティビティ画面


サブ・アクティビティでは、受け取った ParcelableTest オブジェクトより、文字列 (String) と画像 (Bitmap) を取得し、表示します。

    サブ・アクティビティ画面


以下はソースです。

まずは、メイン・アクティビティのレイアウト・ファイルです。
ボタン1個しかありません。

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
<Button
  android:id="@+id/parceble_button"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Parcelableの受け渡しテスト"
  />
</LinearLayout>


次は、アクティビティ間で受け渡しをするオブジェクト用クラスです。
ポイントは以下の2つです。
  1. Parcelable.Creator インターフェースを実装したオブジェクトを CREATOR という名前で作成する。

  2. writeToParcel(Parcel dest, int flags) と ParcelableTest(Parcel in) で読み書きするオブジェクトの順番は合わせる。

1. は android の API Reference にそうしろと書いてあります。
Parcelable
http://developer.android.com/reference/android/os/Parcelable.html

2. ですが、順番が合っていないと以下の Exception が発生しました。
(一部文字化けたままを引用)
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: Parcelable縺ョ繝・せ繝・
    at android.os.Parcel.readParcelable(Parcel.java:1822)

ParcelableTest.java
package jp.taki.parceble;

import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;

public class ParcelableTest implements Parcelable {
  public String mTitle;
  public Bitmap mBitmap;
  public static final String PARCELABLE_TEST = "ParcelableTest";

  public ParcelableTest(String title, Bitmap bitmap) {
    this.mTitle = title;
    this.mBitmap = bitmap;
  }

  @Override
  public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(mTitle);
    dest.writeParcelable(mBitmap, flags);
  }

  public static final Parcelable.Creator<ParcelableTest> CREATOR = new Parcelable.Creator<ParcelableTest>() {
    public ParcelableTest createFromParcel(Parcel in) {
      return new ParcelableTest(in);
    }

    public ParcelableTest[] newArray(int size) {
      return new ParcelableTest[size];
    }
  };

  private ParcelableTest(Parcel in) {
    mTitle = in.readString();
    mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
  }

}


次はメイン・アクティビティです。

画像ファイルはデフォルトで用意される icon.png (ドロイド君の画像) を利用しています。

ボタンを押すと、上記 ParcelableTest オブジェクトを作成し、Intent にセットして、
SubActivity を起動しています。

IntentParcelableTestActivity.java
package jp.taki.parceble;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class IntentParcelableTestActivity extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Resources r = getResources();
    final Bitmap b = BitmapFactory.decodeResource(r, R.drawable.icon);

    Button btn = (Button) findViewById(R.id.parceble_button);
    btn.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {

        ParcelableTest p = new ParcelableTest("Parcelableのテスト", b);
        Intent i = new Intent();
        i.putExtra(ParcelableTest.PARCELABLE_TEST, p);
        i.setClass(IntentParcelableTestActivity.this, SubActivity.class);
        startActivity(i);
      }
    });
  }
}


サブ・アクティビティのレイアウト・ファイルです。
文字列をセットする TextView (id=title) と画像をセットする ImageView (id=image) を配置しています。

sub.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Parcelableデータを以下に表示"
  />

<TextView
  android:id="@+id/title"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  />

<ImageView
  android:id="@+id/image"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  />

</LinearLayout>


サブ・アクティビティです。

Intent より ParcelableTest オブジェクトを取り出し、
画面上の TextView と ImageView にセットしています。

SubActivity.java
package jp.taki.parceble;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

public class SubActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.sub);

    Intent i = getIntent();
    ParcelableTest p = i.getParcelableExtra(ParcelableTest.PARCELABLE_TEST);

    TextView title = (TextView) findViewById(R.id.title);
    title.setText(p.mTitle);

    ImageView image = (ImageView) findViewById(R.id.image);
    image.setImageBitmap(p.mBitmap);

  }
}


最後に、マニフェスト・ファイルに、SubActivity を登録して完成です。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.taki.parceble"
    android:versionCode="1"
    android:versionName="1.0">
  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".IntentParcelableTestActivity"
          android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".SubActivity"
          android:label="@string/app_name" />
  </application>
  <uses-sdk android:minSdkVersion="4" />

</manifest>

まあ、今回のように、String 1個と Bitmap 1個を受け渡すだけなら、それぞれ、
putExtra(String name, String value) と putExtra(String name, Parcelable value) を呼び出せば、
もっと簡単だと思うんですけど。。(; ̄ー ̄A アセアセ・・・