Experiments Never Fail

Xamarin.Android で画面遷移時にデータを渡す

前回のポストで HelloWorld を見てみましたが、次は Activity をもう一つ作って画面遷移してみます。

Activity を追加する #

メニュー → 新規 → ファイル と選択するとこんなダイアログが出るので Activity クラス名を入力して決定します。

"new_file_dialog"

すると NextActivity.cs ができます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace HelloXamarinAndroid
{
[Activity (Label = "NextActivity")]
public class NextActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

// Create your application here
}
}
}

作ってくれるのはここまでです。レイアウト用の Next.axml やリソースファイルは別途作成する必要があります。今回は必要ないのでスルーで。

Android本家だと今はウィザードで Javaソース、レイアウトXML、リソースファイルまで作ってくれてしかも AndroidManifest.xml にも書いてくれちゃいます、便利な世の中になりましたね。

ちなみに Xamarin.Android では AndroidManifest.xml への登録は必要ありません。

画面遷移してみる #

画面遷移は本家と同じく Intent を使います。

button.Click += (sender, e) =>
{
// Goto NextActivity
var intent = new Intent(this, typeof(NextActivity));
this.StartActivity(intent);
};

本家だと NextActivity.class としていたところは typeof(NextActivity) になってますね。

自作クラスを Intent に含める #

さて、画面遷移時にデータを渡すという、よくある処理をやってみたいと思います。
データは Card というクラスにします。

namespace HelloXamarinAndroid
{
class Card
{
public string Name { get; set; }
public string Phone { get; set; }
}
}

C# なので、プロパティ作るのも楽でいいですね。getter/setter なんてアホらしくて…。
この Card クラスのインスタンスを、MainActivity から NextActivity に渡したいと思います。

IParcelable を実装したいが? #

Android では、Activity とか Service をまたぐオブジェクトは Percelable インターフェースを実装しなければならないのでした。
「じゃあ Xamarin では?」「IParcelable でしょ」というわけで IParcelable インターフェースを実装するわけですが、こいつが IDisposableIJavaObject を継承していてこれらも実装させられます。

なんか違う・・・とあきらめてググったところ、こんなんでました。

以降、これに習うことにします。

Mono.Android.Export を参照に追加する #

いきなりこれを忘れて、謎のビルドエラーと10分程格闘しました(汗)

"assembly_reference"

ソリューションツリーの参照の右クリックメニューからアセンブリ参照ダイアログが出るので、Mono.Android.Export を見つけて追加します。

Free 版で使ってる人はここで「Trial するか、買うか、どっちかにせいや」と言われます。 #

Free 版はプログラムのサイズに制限があるのですが、アセンブリを追加することでその制限を超えてしまうものと思われます。

IParcelable を実装する #

Card クラスを、IParcelable を実装するのと共に Java.Lang.Object から派生させます。
あーそういうことですか。

namespace HelloXamarinAndroid
{
class Card : Java.Lang.Object, IParcelable
{
public string Name { get; set; }
public string Phone { get; set; }

public Card(string name, string phone)
{
this.Name = name;
this.Phone = phone;
}

#region IParcelable implementation

public int DescribeContents()
{
return 0;
}

public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteString(this.Name);
dest.WriteString(this.Phone);
}

#endregion
}
}

まだ何か足りません。あ、CREATOR だ。

CREATOR フィールドを準備する #

本家では public static final Parcelable.Creator<MyParcelable> CREATOR = … な感じで用意していましたが、Xamarin.Android では次の2点が異なります。

  1. 匿名クラスが使えないので、IParcelableCreator インターフェースを実装したクラスを用意する。
  2. CREATOR フィールドの代わりに IParcelableCreator を返す static メソッドを用意し、[ExportField("CREATOR")] 属性を付ける。

これらを実装すると、 Card クラスはこうなります。

using Java.Interop;

namespace HelloXamarinAndroid
{
class Card : Java.Lang.Object, IParcelable
{
public string Name { get; set; }
public string Phone { get; set; }

public Card(string name, string phone)
{
this.Name = name;
this.Phone = phone;
}

#region IParcelable implementation

public int DescribeContents()
{
return 0;
}

public void WriteToParcel(Parcel dest, ParcelableWriteFlags flags)
{
dest.WriteString(this.Name);
dest.WriteString(this.Phone);
}

#endregion

[ExportField("CREATOR")]
public static IParcelableCreator GetCreator()
{
return new MyParcelableCreator();
}

// Parcelable.Creator の代わり
class MyParcelableCreator : Java.Lang.Object, IParcelableCreator
{
#region IParcelableCreator implementation
Java.Lang.Object IParcelableCreator.CreateFromParcel(Parcel source)
{
var name = source.ReadString();
var phone = source.ReadString();

return new Card(name, phone);
}

Java.Lang.Object[] IParcelableCreator.NewArray(int size)
{
return new Java.Lang.Object[size];
}
#endregion
}
}
}

これでやっと、「受け渡し可能な」クラスが作成できました。

受け渡ししてみる #

まず渡す方。Intent に詰めます。

button.Click += (sender, e) =>
{
var card = new Card("amay", "987-654-3321");

// Goto NextActivity
var intent = new Intent(this, typeof(NextActivity));
intent.PutExtra("card", card); // intent に Card を詰める

this.StartActivity(intent);
};

次に取り出す方。Intent から取り出します。

namespace HelloXamarinAndroid
{
[Activity (Label = "NextActivity")]
public class NextActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

// Create your application here

var card = this.Intent.GetParcelableExtra("card") as Card;

// Show toast
Toast.MakeText(this,
String.Format("name:{0}, phone:{1}", card.Name, card.Phone),
ToastLength.Long).Show();
}
}
}

〜 as Type 句は型が違っていたら null にしてくれる書き方です。
あと Toast。.Show() は忘れずに。

これで実装完了。動かしてみます。

"receive_parcel"

以上、受け渡し可能なクラスの実装方法でした。

まとめ #

参考 #

published at tags: Xamarin Android C#