ViewModelの作り方
次に、ViewModelの作り方を紹介していきます。
ViewModelを継承
ViewModelは一つのクラスとして定義します。その際、androidx.lifecycle.ViewModelクラスを継承します。そして、先述のように、ViewModelはアクティビティに寄り添うように存在することから、原則、一つのアクティビティクラスに対して一つ作成します。
例えば、MainActivity用のViewModelは、MainViewModelとするように、対応関係がわかるようにしておきます。
クラスメンバはUIのデータ処理を記述
ViewModelクラスのメンバとしては、相方となるアクティビティに必要なデータを踏まえた上で、以下のものを定義します。
- UIで扱うデータのprivateフィールド
- 上記フィールドのセッタとゲッタ(アクセサメソッド)
- UIの入出力に必要なデータをやり取りするためのメソッド
例えば、前節で紹介した足し算アプリのMainActivity用のViewModelであるMainViewModelは、Javaではリスト1のようなコードとなります。
public class MainViewModel extends ViewModel { private int _num1 = 0; // (1) private int _num2 = 0; // (2) public int getAns() { // (3) return _num1 + _num2; } public String getAnsStr() { // (4) String ansStr = ""; if(getAns() != 0) { ansStr = String.valueOf(getAns()); } return ansStr; } public String getNum1Str() { // (5) String num1Str = ""; if(_num1 != 0) { num1Str = String.valueOf(_num1); } return num1Str; } public void setNum1Str(String num1Str) { // (6) if(num1Str.equals("")) { _num1 = 0; } else { _num1 = Integer.parseInt(num1Str); } } public String getNum2Str() { // (7) String num2Str = ""; if(_num2 != 0) { num2Str = String.valueOf(_num2); } return num2Str; } public void setNum2Str(String num2Str) { // (8) if(num2Str.equals("")) { _num2 = 0; } else { _num2 = Integer.parseInt(num2Str); } } public int getNum1() { // (9) return _num1; } public void setNum1(int num1) { // (9) _num1 = num1; } public int getNum2() { // (9) return _num2; } public void setNum2(int num2) { // (9) _num2 = num2; } }
足し算アプリでは、入力された2個の数値が、フィールドとして定義される必要があります。これらを定義しているのが(1)の_num1と(2)の_num2です。そして、それぞれのアクセサメソッドが(9)です。さらに、これらのフィールドをもとに足し算処理を行なっているのが(3)です。
ただし、EditTextにしても、TextViewにしても、実際に画面とのデータのやり取りは文字列です。そのため、文字列と整数値とのやり取りを可能にしているメソッドが(4)〜(8)です。(4)が足し算結果を文字列に変換しているメソッドです。
(5)と(6)が_num1に対しての文字列と整数値の相互変換、(7)と(8)が_num2に対しての文字列と整数値の相互変換のメソッドです。それぞれのメソッドにおいて、文字列が空文字("")の場合や整数値が0の場合などの処理も含め、問題なくデータを受け取ったり表示したりできるようにしています。
Kotlinはプロパティを利用
Kotlinコードでも、ViewModelは同様の作りとなり、リスト2のようになります。
class MainViewModel : ViewModel() { var num1 = 0 // (1) var num2 = 0 // (2) fun getAns(): Int { // (3) return num1 + num2; } fun getAnsStr(): String { // (4) var ansStr = "" if(getAns() != 0) { ansStr = getAns().toString() } return ansStr } fun getNum1Str(): String { // (5) var num1Str = "" if(num1 != 0) { num1Str = num1.toString() } return num1Str } fun setNum1Str(num1Str: String) { // (6) if(num1Str.equals("")) { num1 = 0 } else { num1 = num1Str.toInt() } } fun getNum2Str(): String { // (7) var num2Str = "" if(num2 != 0) { num2Str = num2.toString() } return num2Str } fun setNum2Str(num2Str: String) { // (8) if(num2Str.equals("")) { num2 = 0 } else { num2 = num2Str.toInt() } } }
リスト2のJavaコードをそのままKotlinコードに置き換えたような内容になっていますが、1点大きく違うのが、アクセサメソッドがないことです。Kotlinの場合は、(1)と(2)のようにプロパティを定義するだけで、自動的にアクセサメソッド経由でのデータのやり取りとなるからです。
他のコードは、Javaコードと同じく、(3)が足し算処理を行うメソッドであり、(4)がその結果の文字列を取得するメソッドです。(5)と(6)がnum1に対しての文字列と整数値の相互変換メソッド、(7)と(8)がnum2に対しての文字列と整数値の相互変換メソッドです。
このように、ViewModelを定義すると、UIに必要なデータとその処理が、全てViewModelクラスに記述されることになります。