楽しいアプリ制作の会#13でMVVMをしゃべってきた
たのアプ#13を開催し無事に終了してきました。
今回は「はじめてのMVVM」というセッションをやりました。
・MVVM概要
・MVVMで重要なViewModelの役割
・バインディング
・MVVM Light Toolkit
というポイントを説明してきました。
バインディングの利点は説明だけだと分かりにくいのではないかと思い、擬人化デモ(寸劇)しながら
説明したところ割と評判よかったですw
資料はこちらです。
たのアプ#13を開催し無事に終了してきました。
今回は「はじめてのMVVM」というセッションをやりました。
・MVVM概要
・MVVMで重要なViewModelの役割
・バインディング
・MVVM Light Toolkit
というポイントを説明してきました。
バインディングの利点は説明だけだと分かりにくいのではないかと思い、擬人化デモ(寸劇)しながら
説明したところ割と評判よかったですw
資料はこちらです。
2/25に大阪で開催された「わんくま同盟 大阪勉強会 #47」でセッションしてきました。「HTML5ネタを何かやってよ!」ってお願いをされていたのですが、そこは海のように広いHTML5の世界。50分のセッションでは収拾がつかないと思い、今回もSencha Touchを担いでいきました。資料は以下です。
2ヶ月ぶりのセッションで喋りの感覚が鈍ってました(汗 しゃべる内容の順番が一部前後したりして聞き苦しかったところがあると思います。すみません。。。次はもっと分かりやすく伝えれるように頑張ります。
そういえば、、、お昼休みの直前に急遽LTセッションの依頼があり、LT資料を即席で作って喋りましたb 「楽しいアプリ制作の会」の宣伝です。
こっちは笑いを取れたので満足ですw
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ValidEditText"> <attr name="required" format="boolean" /> <attr name="required_message" format="string" /> </declare-styleable> </resources> |
declare-styleable name=”xxxxxxxx” のxxxxxxxxは、これから作成するカスタムコンポーネントのクラス名を指定しておきましょう。
1 2 3 4 5 6 7 | public class ValidEditText extends EditText { public ValidEditText(Context context, AttributeSet attrset) { super(context, attrset); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class ValidEditText extends EditText { // 必須チェック有無 private boolean required; // 必須チェックエラー時のエラーメッセージ private String required_message; // コンストラクタ public ValidEditText(Context context, AttributeSet attrset) { super(context, attrset); //----- layout.xmlに記述される属性を取得する ----- // /res/values/attrs が R.styleable として参照できる // 先に定義した ValidEditText の属性名をすべて取得する TypedArray attrsarray = context.obtainStyledAttributes(attrset, R.styleable.ValidEditText); //----- ValidEditTextの属性値を個別に取得する ----- // 必須チェック有無 // 属性値がbooleanの場合は、第2引数で初期値を指定可能 required = attrsarray.getBoolean(R.styleable.ValidEditText_required, false); // 必須チェックエラー時のエラーメッセージ // 属性値がStringの場合、layout.xmlの属性値は @string/xxxxx 形式で記述可能 required_message = attrsarray.getString(R.styleable.ValidEditText_required_message); } } |
AttributeSet#obtainStyledAttributes で、attrs.xmlで定義した属性を一挙に取得できます。またTypedArray#getBooleanやTypedArray#getStringで属性値を取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Validation public boolean isValid(Context context) { // 必須チェック if (required == true) { if (getText().length() == 0) { // 何も入力されていないのでエラーを表示する Toast.makeText(context, required_message, Toast.LENGTH_LONG).show(); return false; } } return true; } |
1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, Main!</string> <string name="app_name">CustomComponent</string> <string name="required_message">何か入力してね!</string> </resources> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <jp.tworks.android.customcomponent.widget.ValidEditText android:id="@+id/validTextEdit1" android:layout_width="fill_parent" android:layout_height="wrap_content"> <requestFocus /> </jp.tworks.android.customcomponent.widget.ValidEditText> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tworks="http://schemas.android.com/apk/res/jp.tworks.android.customcomponent" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <jp.tworks.android.customcomponent.widget.ValidEditText android:id="@+id/validEditText1" android:layout_width="fill_parent" android:layout_height="wrap_content" tworks:required="true" tworks:required_message="@string/required_message" > <requestFocus /> </jp.tworks.android.customcomponent.widget.ValidEditText> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> |
XMLネームスペースの追加は、「xmlns:tworks=”http://schemas.android.com/apk/res/jp.tworks.android.customcomponent”」のように、http://schemas.android.com/apk/res/[アプリケーションのパッケージ名] という書き方になります。ValidEditTextの属性追加は「XMLネームスペース:属性=”属性値”」のような記述になります。ここでは「tworks:required=”true”」「tworks:required_message=”@string/required_message”」としました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Main extends Activity { private ValidEditText validEditText; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // ValidEditTextのインスタンスを取得 validEditText = (ValidEditText)findViewById(R.id.validEditText1); // Buttonクリック時にValidEditTextのisValid()を実行する findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { validEditText.isValid(Main.this); } }); } } |


1 2 3 4 5 6 7 8 9 | <?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Android Binding</string> <string name="hint_yourheight">Enter your Height</string> <string name="hint_yourweight">Enter your Weight</string> <string name="caption_calcbmi">Calc BMI</string> </resources> |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:hint="@string/hint_yourheight"> <requestFocus /> </EditText> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:hint="@string/hint_yourweight"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/caption_calcbmi" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" /> </LinearLayout> |
通常は各ウィジットにandroid:id=”@+id/hoge”という感じでIDを振りコードからfindViewById(”hoge”)という感じで参照します。Android BindingではBindingによる参照を行うため、潔く取っ払いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package jp.tworks.android.bmi; import android.view.View; import gueei.binding.Command; import gueei.binding.observables.StringObservable; public class BmiModel { // for EditText TextProperty public StringObservable Height = new StringObservable(); public StringObservable Weight = new StringObservable(); public StringObservable Bmi = new StringObservable(); // for Button on Click public Command calcBmi = new Command() { public void Invoke(View view, Object... args) { int height = Integer.valueOf(Height.get()); int weight = Integer.valueOf(Weight.get()); int bmi = (int)(weight / Math.pow(height / 100.0, 2)); Bmi.set(String.valueOf(bmi)); } }; } |
画面に配置した各ウィジットの属性(Property)と紐付け(Binding)するものはObservable(を継承したクラス)、onClickなどのイベントはCommandで実装します。EditTextに入力された情報はtextプロパティ(文字列型)で参照するため、StringObservableの変数としました。BMI計算結果を表示するTextViewについてもtextプロパティ(文字列型)ですのでStringObservable、Buttonのクリックイベントを処理するCommand名をcalcBmiとしました。
続いてCommandのInvokeにBMI計算のロジックを記述します。StringObservableのHeight/Weightから値を取り出し、計算した結果をStringObservableのBmiに設定しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:binding="http://www.gueei.com/android-binding/" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:hint="@string/hint_yourheight" binding:text="Height"> <requestFocus /> </EditText> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="number" android:hint="@string/hint_yourweight" binding:text="Weight" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/caption_calcbmi" binding:onClick="calcBmi" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" binding:text="Bmi" /> </LinearLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package jp.tworks.android.bmi; import gueei.binding.app.BindingActivity; import android.os.Bundle; public class BmiActivity extends BindingActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.main); // モデルを生成 BmiModel model = new BmiModel(); // Layout.xmlの binding:xxx と modelを関連付ける setAndBindRootView(R.layout.main, model); } } |
setContentViewの代わりにsetAndBindRootViewというメソッドを使用します。このタイミングでViewとModelが実際に紐づきます。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 | package jp.tworks.android.bmi; import gueei.binding.Binder; import android.app.Application; public class BmiApplication extends Application { @Override public void onCreate() { super.onCreate(); Binder.init(this); } } |
Android.Mainfestの「Application Attributes」–>「Name」に、追加したクラス名を設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package jp.tworks.android.bmi; import android.view.View; import gueei.binding.Command; import gueei.binding.observables.StringObservable; import gueei.binding.validation.ModelValidator; import gueei.binding.validation.ValidationResult; import gueei.binding.validation.validators.Required; public class BmiModel { // for EditText TextProperty @Required(ErrorMessage = "Height is required.") public StringObservable Height = new StringObservable(); @Required(ErrorMessage = "Weight is required.") public StringObservable Weight = new StringObservable(); public StringObservable Bmi = new StringObservable(); // for Button on Click public Command calcBmi = new Command() { public void Invoke(View view, Object... args) { // アノテーションで指定した入力チェックを実行する ValidationResult result = ModelValidator.ValidateModel(BmiModel.this); if (result.isValid()) { // Validation OK int height = Integer.valueOf(Height.get()); int weight = Integer.valueOf(Weight.get()); int bmi = (int)(weight / Math.pow(height / 100.0, 2)); Bmi.set(String.valueOf(bmi)); } else {// Validation NG // エラーを表示 String message = ""; for(String error : result.getValidationErrors()){ message += error + "\n"; Bmi.set(message); } } } }; } |
参考書「はじめてのWindows Phoneプログラミング」を執筆しました。蜜葉たんと共同執筆です。
http://www.kohgakusha.co.jp/books/detail/978-4-7775-1661-2

タイトルの通り、これから”はじめて”Windows Phoneプログラミングをする方向けの内容になっています。
アプリを出している人が読む本じゃないんだからねっ!
さておき、立ち読みして良かったら買ってくださいねー。
Windows Phone Advent Calendar 20日目の記事です。
■はじめに
Windows Phoneのアプリケーションに、アプリ内テーマを適用する方法を紹介します。参考文献はコチラです。
複数の画面を持つアプリケーションで、各画面に配置しているボタンやチェックボックスなどの配色をカスタマイズしつつ、全体で統一したいということがあります。画面毎/コントロール毎にプロパティを編集してもできますが、色味を変えたくなった時に、その都度すべてのコントロールのプロパティを編集しなければならないのは大変なことです。
Androidは自身のアプリケーションの外観を統一させるために、Style Resourceという機能があります(Android UI前身のSWINGがその機能を持っており、それを継承していると思われます)。Style Resourceにデザイン要素を定義し、それを参照している画面はStyle Resourceのデザイン定義に従って描画を行います。
Windows Phoneで同じことができないかと調べたところ、冒頭の文献を見つけました。
■開発手順

1 2 3 4 5 6 7 8 | <!--アプリケーション リソース--> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Theme/System.Windows.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | using System.Collections; public App() { ... InitializeComponent(); // 以下を追加する MergeCustomColors(); InitializePhoneApplication(); ... } // テーマをマージするロジック private void MergeCustomColors() { var dictionaries = new ResourceDictionary(); // TODO:自身のアプリケーション名に合わせること string source = @"/InAppTheme;component/Theme/ThemeResources.xaml"; var themeStyles = new ResourceDictionary { Source = new Uri(source, UriKind.Relative) }; dictionaries.MergedDictionaries.Add(themeStyles); ResourceDictionary appResources = App.Current.Resources; foreach (DictionaryEntry entry in dictionaries.MergedDictionaries[0]) { SolidColorBrush colorBrush = entry.Value as SolidColorBrush; SolidColorBrush existingBrush = appResources[entry.Key] as SolidColorBrush; if (existingBrush != null && colorBrush != null) { existingBrush.Color = colorBrush.Color; } } } |
1 2 3 4 5 6 7 8 | <Color x:Key="PhoneAccentColor">#FFF5B80C</Color> <SolidColorBrush x:Key="PhoneAccentBrush" Color="{StaticResource PhoneAccentColor}"/> <Color x:Key="PhoneForegroundColor">#FF86FF02</Color> <SolidColorBrush x:Key="PhoneForegroundBrush" Color="{StaticResource PhoneForegroundColor}"/> <Color x:Key="PhoneRadioCheckBoxCheckColor">#FFF50CD8</Color> <SolidColorBrush x:Key="PhoneRadioCheckBoxCheckBrush" Color="{StaticResource PhoneRadioCheckBoxCheckColor}"/> |
■所感
簡単な手順でテーマが設定できました。テーマで配色が管理できるのはとても便利ですね!しかし配色をあまりに変更してしまうと、他のアプリケーションと見栄えがかけ離れてしまい、エンドユーザーにそぐわないアプリケーションになり兼ねません。コーポレートカラー/ブランドカラーなどワンポイントに留める方がよさそうな気がしました。
ただし、アクセシビリティへの対応には結構使えるんじゃないでしょうか。このBlog執筆時点でWindows Phoneはアクセシビリティ対応に不十分で、色弱などの視覚障がい者に優しくないアプリケーションがあるかもしれません。そのような方向けの配色を考慮したアプリケーションを作成するときに、役立つと思います。
ソースコードはこちら
InAppTheme.zip
今年最後の「楽しいアプリ制作の会」を開催します!
テーマ「楽しいLTの会 2011」。関西圏の勉強会主催者さんを中心に、楽しいLTセッションを行う催しです。
詳細な告知はコチラでやってます。参加お待ちしてますー。
メインマシンのMacBook Pro 17′ (2009 Mid)のWindows 7 (BootCamp)で、ずっと困ってたことが解決したので備忘録を残します。
ココからドライバをダウンロードしてインストールで解決しました。
何が困っていたかというと、BootCamp(Mac OS X 10.6)が当ててくれるGPU(GeForce 9600M GT) のドライバがWDDM 1.0だったんですね。WDDM 1.0で何が困るかというと、Windows Phone 7開発で使うエミュレータがまともに動かないのですよ。エミュレータがまともに動く要件に、WDDM 1.1対応のGPUってのがありまして。
まぁ普段の開発は実機があるので手詰まりになることはないのですが、勉強会などでプロジェクタ越しにUI/UXを見せたいときはエミュレータが要るのですよね。12/4に開催する「Windows Phone Hackathon in Osaka」に間に合ってホッとしています。
次はコンソーシアムでしゃべります!
先端IT活用推進コンソーシアムというのがありまして、それの分科会の「ユーザーエクスペリエンス技術部会」の「第1回「ユーザーエクスペリエンス技術部会」&
「ネットデバイスアプリケーション部会」合同キックオフセミナー」(長っ!)でiOS/AndroidのUI/UXをしゃべります。
セッションの講師陣が、僕以外はみなさん凄いですよ。っていうか僕だけ浮いてません?そしてセッションの順番が非常に良くない(汗)。UI/UXについて、いつも興味深く分かりやすいお話をしてくださるMicrosoft川西さんの後ってのがとってもプレッシャーです(汗)。でもいい機会を頂けたので、精一杯やってみようと思います。
11/19に開催された「HTML5 プログラミング生放送勉強会 第11回@大阪」で、JavaScriptのスマートフォンWebアプリ開発フレームワーク「Sencha Touch」についてのセッションをやってきました。
詳細なレポートはプロ生主催者の5jzさん(@jz5)のレポートを見て頂ければと思います。
今回は資料作成に時間をかけることが出来ずSencha Touchの上辺しか語れなかったのですが、セッション後のアンケートを見るとさほど悪くないというか、厳し目に見ても普通くらいの評価でした。コーディングしつつデモを見せつつ〜という流れで進めたのが、分かりやすさに繋がったのかな。。。
最後に、今回のお話を下さいました、プロ生主催者の5jzさん(@jz5)、なかじさん(@nakaji)本当にありがとうございました!またよろしくお願いします。
資料は以下です。(slideshare)