はじめに
開発案件でDataBindingを使っており、学習したことを記事にする。
今回はDataBindingを使って、データクラスオブジェクトのプロパティの変更を監視してView表示に反映させるまでを学習した。
今回作成した学習用アプリ
①と②のテキスト表示を「チェンジ」ボタンを押す毎に動的に行ったりきたり切り替えるというもの
「ドラえもん」⇄「のびた」
① |
---|
② |
---|
実装ファイル
build.gradle(:app)
activity_main.xml
-
Character.java
(モデルclass) -
EventHandlers.java
(インターフェース) MainActivity.java
実装していく
1. DataBinding の導入
build.gradle(:app)
にdataBinding { enabled = true }
を追加
apply plugin: 'com.android.application'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "com.android.databindingjava"
minSdkVersion 24
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// 記述を追加
dataBinding {
enabled = true
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
2. 変更を監視するモデルクラスを定義
// BaseObservableを継承
public class Character extends BaseObservable {
private String name;
public Character(String name) {
this.name = name;
}
// getNameに@Bindableを付与することにより監視用の定数BR.nameが生成される
@Bindable
public String getName() {
return name;
}
// setNameにnotifyPropertyChanged(BR.name)を付与することで
// レイアウト側からBR.nameに対応するgetName()が呼ばれる(setNameされるタイミングでgetNameがレイアウト側から呼ばれる)
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
}
viewに変更を反映させるために、
-
BaseObservable
を継承 -
getName
に@Bindable
をつけ監視用の定数であるBR.name
を生成する -
setName
にnotifyPropertyChanged(BR.name);
を記述する
こうすることでsetName
が実行されるタイミングでgetName
がレイアウト側から呼ばれ、name
の値を変更した際にviewに変更が反映されるようになる。
3. データを変更するためのインターフェースを定義
public interface EventHandlers {
// クリックイベントに対応させたいため、引数はView.OnClickListenerのonClickと同じ(View view)にする
void onChangeClick(View view);
}
レイアウトにセットするイベントハンドラーをインターフェースとして定義
4. 変更を反映させるレイアウトファイルを作成
<?xml version="1.0" encoding="utf-8"?>
<!-- ルートをlayoutにすることでDataBindingに対応したレイアウトとして認識される -->
<!-- activity_main.xml => ActivityMainBinding このような形で自動的にxmlファイル名に応じたBindingクラスが作られる-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- Binding オブジェクト -->
<data>
<!-- この記述によりcharacterという名前(任意)で、Userクラスオブジェクトとの結びつけがされる -->
<variable name="character" type="com.android.databindingjava.Character" />
<!-- この記述によりeventHandlersという名前(任意)で、ハンドラー(インターフェース)が設定される -->
<variable name="eventHandlers" type="com.android.databindingjava.EventHandlers" />
</data>
<!-- Views-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_view_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@{character.name}" />
<Button
android:id="@+id/button_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30dp"
android:text="チェンジ"
android:onClick="@{eventHandlers.onChangeClick}"
app:layout_constraintBottom_toTopOf="@id/text_view_user_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
レイアウトからモデルclassへのアクセス
<variable name="character" type="com.android.databindingjava.Character" />
により、レイアウトファイル内でCharacterクラスオブジェクトをcharacterという名前で定義しており、レイアウトファイル内でオブジェクトを使用できるようになる。
@{character.name}
この記述でCharacterクラスのnameプロパティにアクセスでき、android:text="@{character.name}"
によりtextにプロパティの値がセットされる。
※ @{}
の中身はnullを許容するようになっており、nullの場合でもNullPointerExceptionが発生することはない。
レイアウト要素にイベントハンドラーをセット
<variable name="eventHandlers" type="com.android.databindingjava.EventHandlers" />
により、レイアウトファイル内でEventHandlersインターフェースをeventHandlersという名前で定義しており、レイアウトファイル内でインターフェースにアクセスできるようになる。
@{eventHandlers.onChangeClick}
この記述でEventHandlersインターフェースのonChangeClickにアクセスでき、Button要素の中でandroid:onClick="@{eventHandlers.onChangeClick}"
を記述することによりクリックした際にonChangeClickが呼ばれるようになる。
(※ 後述するMainActivity.javaへの記述も必要)
5. MainActivityでDataBinding処理を定義
// EventHandlers(インターフェース) を実装
public class MainActivity extends AppCompatActivity implements EventHandlers {
private Character chara = new Character("ドラえもん");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// activity_main.xml に対応したクラスの bindingインスタンスを作成
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// activity_main.xmlのcharacterにcharaをセット
binding.setCharacter(chara);
// activity_main.xmlのeventHandlersにMainActivityをセット
binding.setEventHandlers(this);
}
// button_changeのクリックイベント処理(インターフェース)
@Override
public void onChangeClick(View view) {
// charaのnameの文字列によって、セットする文字列を変える
if (chara.getName().equals("ドラえもん")) {
chara.setName("のびた");
} else {
chara.setName("ドラえもん");
}
}
}
ルートを<layout>
にしたレイアウトファイルを作成することで自動的にxmlファイル名に応じたBindingクラスが作成される。
今回であれば、 activity_main.xml => ActivityMainBinding(.java)
onCreate
の中では以下のような処理を行っている
-
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
でインスタンスを作成 -
binding.setCharacter(chara);
でレイアウトファイルのcharacterにcharaをセット -
binding.setEventHandlers(this);
でレイアウトファイルのeventHandlersにMainActivityをセット
そしてインターフェースonChangeClickを実装し、メソッドないでTextView文字列値に応じてデータを変更する処理を書いている。
参考サイト
私のこの記事はこちらの記事をめちゃくちゃ参考にさせていただいております。
本当にわかりやすかったです!ありがとうございました!
最後に
今回は簡単なアプリですが、実際の案件は規模が大きくコードを読み解くのが大変なのが現状です。
さらに学習継続していきます。
誤り、ご指摘などあればコメントいただければ幸いです。