Android Binding

さて昨日の続きで引き続きAndroid Bindingで遊んでみます。次の題材は電卓アプリです。元ネタはこちら。

Build a simple Android calculator with MVVM (& Android Binding!)


電卓アプリは一般にボタンが多いので、MVC (MVVM) パターンを適用するにはモッテコイの題材ですね。ちなみに見た目はこんな感じです。


まずは計算結果を表示するTextViewから見ていきます。

<!-- 計算結果 -->
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:textSize="45dp"
    android:gravity="right|bottom"
    binding:text="FormattedDisplay" />


バインド先 (FormattedDisplay) はこんな感じ。

// 計算結果表示用プロパティ
public final Observable<Double> Display = new Observable<Double>(Double.class, 0d);
public final DependentObservable<String> FormattedDisplay = new DependentObservable<String>(String.class, Display) {
    @Override
    public String calculateValue(Object... arg0) throws Exception {
        DecimalFormat format = new DecimalFormat();
        format.applyPattern("#.######");
        String output = format.format(Display.get());
        if(output.length() <= MAXLENGTH) {
            return output;
        }
        format.applyPattern("#.########E00");
        return format.format(Display.get());
    }
};


DependentObservableという新しいオブザーバ (Property) があります。これは上のDisplayプロパティに依存したプロパティであることを意味します。つまりDisplayプロパティに変化があれば、このFormattedDisplayプロパティにもその変更が伝播する、という仕組みです。単純な情報を加工してViewとバインドする際に使われます。まさしくこれがViewModelコンポーネントですね。

次は数字ボタン。レイアウトファイルから。

<Button android:text="7" binding:onClick="Number7" />
<Button android:text="8" binding:onClick="Number8" />
<Button android:text="9" binding:onClick="Number9" />
<!-- などなど -->


そしてバインド先 (Number7, 8, 9, ...) 。

// 数字ボタン用コマンド
public NumberCommand Number9 = new NumberCommand(9);
public NumberCommand Number8 = new NumberCommand(8);
public NumberCommand Number7 = new NumberCommand(7);
// などなど

// 数字ボタンアクションに対応するコマンドクラス
private class NumberCommand extends Command {    // CommandはInterfaceじゃない!
    private int _number = 0;

    public NumberCommand(int number) {
        _number = number;
    }

    @Override
    public void Invoke(View arg0, Object... arg1) {
        addNumber(_number);
    }
}


Commandは以前はインターフェイスだったようですが、現バージョンでは抽象クラスなので注意が必要です。

そして最後に演算ボタンを見てみます。

<Button android:text="/" binding:onClick="Divide" />
<Button android:text="X" binding:onClick="Multiply" />
<Button android:text="-" binding:onClick="Minus" />
<Button android:text="+" binding:onClick="Plus" />


バインド先 (Divide, Multiply, Minus, Plus) はこんな感じ。

// +ボタン用コマンド
public OperatorCommand Plus = new OperatorCommand(new Operator(1, "+", false) {
    @Override
    public double calculate(double operandA, double operandB) {
        return operandA + operandB;
    }
});
	
// ーボタン用コマンド
public OperatorCommand Minus = new OperatorCommand(new Operator(1, "-", false) {
    @Override
    public double calculate(double operandA, double operandB) {
        return operandA - operandB;
    }
});
	
// ×ボタン用コマンド
public OperatorCommand Multiply = new OperatorCommand(new Operator(2, "x", false) {
    @Override
    public double calculate(double operandA, double operandB) {
        return operandA * operandB;
    }
});
	
// ÷ボタン用コマンド
public OperatorCommand Divide = new OperatorCommand(new Operator(2, "/", false) {
    @Override
    public double calculate(double operandA, double operandB) {
        return operandA / operandB;
    }
});
	
// =ボタン用コマンド
public OperatorCommand Equal = new OperatorCommand(new Operator(0, "=", true) {
    @Override
    public double calculate(double operandA, double operandB) {
        return operandA;
    }
});

// 演算ボタンアクションに対応するコマンドクラス
private class OperatorCommand extends Command {
    private final Operator _operator;

    public OperatorCommand(Operator operator) {
        _operator = operator;
    }

    @Override
    public void Invoke(View arg0, Object... arg1) {
        operate(_operator);
    }
}


NumberCommandクラスと同様に、OperatorCommandクラスもCommand抽象クラスを拡張して定義しています。コンストラクタの引数でバリエーションを増やすやり方は常套手段のようですね。

いつものようにこちらにコードを上げておきます。ご参考下さいませ。

サンプルコード置き場


というわけで、2つのAndroid Bindingの事例を見て来ました。とても簡単な事例ですが、それでもとても興味深い内容でした。これ、日常的に使えるようになりたいなぁ。また週末やろ。