SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門

画面へのデータ反映処理を自動化できるデータバインディング! コードのバリエーションを紹介

一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門 第7回


  • X ポスト
  • このエントリーをはてなブックマークに追加

データバインディングとエンティティ

 前節の最後に式言語を利用したコードの問題点を指摘しました。その解決策を紹介する前に、それにつながるデータバインディングの仕組みを先に紹介していきます。

データ項目が増えたサンプル

 前節までのサンプルでは、データバインディングとして利用するデータ項目は、randNumという乱数ひとつのみでした。このように画面に表示するデータがひとつだけなのは稀であり、複数のデータ項目を扱うのが通常です。そこで、データ項目を増やしたサンプルを用意しました。その画面は、図1のようなものです。ボタンをタップすると、0から10の間の乱数を2個発生させ、それを四角形の縦の長さと横の長さとします。それらの値をもとに面積を計算し、表示させるものです。これだけで画面に関与するデータ項目が3個に増え、さらに、ボタンも含めると、関与する画面部品が4個となります。

図1: 乱数を表示させるアプリの画面
図1: 乱数を表示させるアプリの画面

 この画面の表示データとして縦の長さのheight、横の長さのwidth、面積のareaとすると、データバインディングの設定をレイアウトxmlに記述するにあたって、variableタグを、height、width、areaの3個定義しても問題なく動作します。しかし、当然ですがデータ項目が増えると、それだけコードが煩瑣になるだけでなく、後述するように不便なことも起こります。

エンティティオブジェクトの利用

 そこで、画面表示に必要なデータ項目をひとつのオブジェクトとし、まとめてデータを反映させます。例えば、今回のサンプルでは、データをまとめておくクラスとして、Rectangleのようなものを用意するとします。このRectangleのようなオブジェクトを、データオブジェクトと言ったり、エンティティと言ったりします(本稿ではエンティティと記載していきます)。Rectangleエンティティクラスの構造に関しては後述するとして、このRectangleを使ってデータバインディングを設定すると、レイアウトxmlはリスト1のような内容となります。

リスト1:エンティティを利用したデータバインディングの設定コード
<?xml version="1.0" encoding="utf-8"?>
<layout
  xmlns:android="http://schemas.android.com/apk/res/android"
  …>
  <data>
    <variable
       name="rectangle"
       type="com.….Rectangle" />  (1)
  </data>
  <androidx.constraintlayout.widget.ConstraintLayout
    …>
     :
    <TextView
      android:id="@+id/tvHeight"
      :
      android:text="@{rectangle.heightStr}"/>  (2)
     :
    <TextView
      android:id="@+id/tvWidth"
      :
      android:text="@{rectangle.widthStr}"/>  (3)
     :
    <TextView
      android:id="@+id/tvArea"
      :
      android:text="@{rectangle.areaStr}"/>  (4)
     :
  </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 リスト1のポイントは、(1)です。variableタグのデータ型を指定するtype属性に、エンティティクラスの完全修飾名を指定します。これだけで、name属性で指定した変数名に対して.(ドット)でアクセスするだけで、エンティティオブジェクト内に格納されたデータをレイアウト記述内で利用できるようになります。例えば、リスト1の(2)ではrectangle.heightStrという記述で、RectangleエンティティオブジェクトであるrectangleのheightStrプロパティにアクセスし、そのデータを表示します。結果、縦の長さが表示されます。横の長さの(3)、面積の(4)も同様です。

エンティティクラスの作り方(Java版)

 ところで、リスト1では、(2)の縦の長さを表すプロパティが、heightではなくheightStrとなっています。(3)の横の長さ、(4)の面積も同様です。その種明かしも含めて、ここで、Rectangleのようなエンティティクラスの作り方を紹介します。Rectangleクラスは、Javaではリスト2のようなコードになります。

リスト2:Rectangleクラス(Java版)
public class Rectangle {
  private final int _height;  // (1)
  private final int _width;  // (2)
  public Rectangle(int height, int width) {  // (3)
    _height = height;
    _width = width;
  }
  public int getArea() {  // (4)
    return _height * _width;
  }
  public String getAreaStr() {  // (5)
    return String.valueOf(getArea());
  }
  public int getHeight() {  // (6)
    return _height;
  }
  public int getWidth() {  // (7)
    return _width;
  }
  public String getHeightStr() {  // (8)
    return String.valueOf(_height);
  }
  public String getWidthStr() {  // (9)
    return String.valueOf(_width);
  }
}

 まず、エンティティクラスを作成する際の注意点は、イミュータブル(不変)オブジェクトとして作る必要があることです。そのため、縦の長さを表すリスト2の(1)と横の長さを表す(2)のように、フィールドはfinalキーワードをつけて、値を変更できないようにしておきます。

 そして、(3)のようにコンストラクタでそれらのフィールドの値を受け取るようにしておきます。また、フィールドの値を変更するセッタも定義しません。このコードパターンのおかげで、エンティティクラスをnewした際に渡したデータが、その後変更できないようになります。

 一方、フィールドの値を利用したゲッタはさまざま用意できます。リスト2では、単にフィールドの値をリターンするゲッタを、(6)と(7)に定義しています。また、面積に関しても、面積を保持するフィールドを用意するのではなく、(4)のようにその都度計算した値をリターンするゲッタを用意することで対応します。

 データバインディングでは、これらのゲッタをレイアウトxmlで利用する場合、実は、以下のようなコードを記述する必要はありません。

@{rectangle.getArea()}

 単に、プロパティアクセスのように、以下のコードを記述するだけです。

@{rectangle.area}

 このことから、リスト1の(2)などのコード、すなわちheightStrプロパティなどへのアクセスのカラクリが理解できると思います。実は、このコードは、リスト2の(8)のgetHeightStr()ゲッタへのアクセスを意味します。

 そして、前節で説明したように、text属性に対して@{ }を記述する場合は、Stringへの変換が必要なため、その変換処理を含んだゲッタを用意したのが、リスト2の(5)と(8)と(9)です。

 このように、データをまとめて保持できるエンティティを用意し、そのデータの計算や加工、Stringへの変換処理も、ゲッタ(加工ゲッタ)の形でそのエンティティに含めてしまうことで、非常にスッキリしたコードとなります。前節の最後に指摘した、式言語に複雑なコードを記述せず、単にプロパティアクセスのコードで済ますための解決法も、この加工ゲッタです。

エンティティクラスの作り方(Kotlin版)

 このエンティティクラスであるRectangleをKotlinでコーディングする場合は、リスト3のようになります。

リスト3:Rectangleクラス(Kotlin版)
data class Rectangle(val height: Int, val width: Int) {  // (1)
  fun getArea(): Int {  // (2)
    return height * width
  }
  fun getAreaStr(): String {  // (3)
    return getArea().toString()
  }
  fun getHeightStr(): String {  // (4)
    return height.toString()
  }
  fun getWidthStr(): String {  // (5)
    return  width.toString()
  }
}

 Kotlinの場合は、Javaと違い、dataクラスという便利な仕組みがあるので、それを利用します。リスト3の(1)のように、class宣言にdataキーワードがついているのは、そのためです。また、コンストラクタの引数をvalで定義することで、イミュータブルオブジェクトが実現できます。

 このように、Kotlinではdataクラスを利用するため、クラス内に定義するメソッドは、Java版であるリスト2の(4)、(5)、(8)、(9)に該当する加工ゲッタのみとなります。リスト3の(2)がリスト2の(4)を、(3)が(5)を、(4)が(8)を、(5)が(9)をそのままKotlinコードに置き換えたコードとなっているのはそのためです。

アクティビティではエンティティをまとめてセット

 このようなエンティティを用意すると、アクティビティ側でのコードは、エンティティをnewしてデータを格納し、まとめてバインディングオブジェクトにセットするものとなります。コードサンプル的には、Javaではリスト4、Kotlinではリスト5のようなコードになります。ただし、※のコードは、通常はViewModelなど、アクティビティ以外のクラスに記述します。

リスト4:アクティビティ側でのエンティティの利用コード(Java版)
int height = (int) (Math.random() * 10) + 1;  // ※
int width = (int) (Math.random() * 10) + 1;  // ※
Rectangle rectangle = new Rectangle(height, width);  // ※
_activityMainBinding.setRectangle(rectangle);
リスト5:アクティビティ側でのエンティティの利用コード(Kotlin版)
val height = (Math.random() * 10).toInt() + 1  // ※
val width = (Math.random() * 10).toInt() + 1  // ※
val rectangle = Rectangle(height, width)  // ※
_activityMainBinding.rectangle = rectangle

次のページ
リスナ登録

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
一歩進んだAndroidアプリ開発ができる「Android Jetpack」入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18644 2023/11/30 16:35

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング