Android
データバインディングライブラリ
サーチ…
備考
セットアップ
データバインディングを使用する前に、 build.gradle
プラグインを有効にする必要があります。
android {
....
dataBinding {
enabled = true
}
}
注:バージョン1.5.0のAndroid Gradleプラグインにデータバインディングが追加されました
バインディングクラス名
データバインディングプラグインは、レイアウトのファイル名をPascalの場合に変換し、最後に「Binding」を追加することによって、バインディングクラス名を生成します。したがって、 item_detail_activity.xml
は、 ItemDetailActivityBinding
という名前のクラスを生成します。
リソース
基本的なテキストフィールドバインド
Gradle(モジュール:app)の設定
android {
....
dataBinding {
enabled = true
}
}
データ・モデル
public class Item {
public String name;
public String description;
public Item(String name, String description) {
this.name = name;
this.description = description;
}
}
レイアウトXML
最初のステップは、レイアウトを<layout>
タグにラップし、 <data>
要素を追加し、データモデルに<variable>
要素を追加することです。
次に、 @{model.fieldname}
を使用してXML属性をデータモデルのフィールドにバインドできます。ここで、 model
は変数の名前で、 fieldname
はアクセスするフィールドです。
item_detail_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="item" type="com.example.Item"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.description}"/>
</LinearLayout>
</layout>
バインディングで正しく設定されたXMLレイアウトファイルごとに、Android Gradleプラグインは対応するクラス:bindingsを生成します。 item_detail_activityという名前のレイアウトがあるため、対応する生成されたバインディングクラスはItemDetailActivityBinding
と呼ばれItemDetailActivityBinding
。
このバインディングは、以下のようにActivityで使用できます。
public class ItemDetailActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ItemDetailActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.item_detail_activity);
Item item = new Item("Example item", "This is an example item.");
binding.setItem(item);
}
}
アクセサメソッドによるバインド
モデルにプライベートメソッドがある場合、データバインディングライブラリではメソッドのフルネームを使用せずにビューにアクセスすることができます。
データ・モデル
public class Item {
private String name;
public String getName() {
return name;
}
}
レイアウトXML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="item" type="com.example.Item"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Since the "name" field is private on our data model,
this binding will utilize the public getName() method instead. -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
</LinearLayout>
</layout>
クラスの参照
データ・モデル
public class Item {
private String name;
public String getName() {
return name;
}
}
レイアウトXML
Javaの場合と同様に、参照されたクラスをインポートする必要があります。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View"/>
<variable name="item" type="com.example.Item"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- We reference the View class to set the visibility of this TextView -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"
android:visibility="@{item.name == null ? View.VISIBLE : View.GONE"/>
</LinearLayout>
</layout>
注:パッケージjava.lang.*
は、システムによって自動的にインポートされます。 ( JVM
for Java
同じです)
フラグメント内のデータバインディング
データ・モデル
public class Item {
private String name;
public String getName() {
return name;
}
public void setName(String name){
this.name = name;
}
}
レイアウトXML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="item" type="com.example.Item"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
</LinearLayout>
</layout>
断片
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FragmentTest binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test, container, false);
Item item = new Item();
item.setName("Thomas");
binding.setItem(item);
return binding.getRoot();
}
組み込みの双方向データバインディング
双方向データバインディングは、次の属性をサポートします。
素子 | プロパティ |
---|---|
AbsListView | android:selectedItemPosition |
CalendarView | android:date |
CompoundButton | android:checked |
DatePicker |
|
EditText | android:text |
NumberPicker | android:value |
RadioGroup | android:checkedButton |
RatingBar | android:rating |
SeekBar | android:progress |
TabHost | android:currentTab |
TextView | android:text |
TimePicker |
|
ToggleButton | android:checked |
Switch | android:checked |
使用法
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<EditText android:text="@={user.firstName}" .../>
</RelativeLayout>
</layout>
バインディング式@={}
は、 双方向バインディングに必要な追加の=
があることに注意してください。双方向バインディング式でメソッドを使用することはできません。
RecyclerViewアダプタでのデータバインディング
また、 RecyclerView
アダプタ内でデータバインディングを使用することもできます。
データ・モデル
public class Item {
private String name;
public String getName() {
return name;
}
}
XMLレイアウト
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.name}"/>
アダプタクラス
public class ListItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Activity host;
private List<Item> items;
public ListItemAdapter(Activity activity, List<Item> items) {
this.host = activity;
this.items = items;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflate layout and retrieve binding
ListItemBinding binding = DataBindingUtil.inflate(host.getLayoutInflater(),
R.layout.list_item, parent, false);
return new ItemViewHolder(binding);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Item item = items.get(position);
ItemViewHolder itemViewHolder = (ItemViewHolder)holder;
itemViewHolder.bindItem(item);
}
@Override
public int getItemCount() {
return items.size();
}
private static class ItemViewHolder extends RecyclerView.ViewHolder {
ListItemBinding binding;
ItemViewHolder(ListItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
void bindItem(Item item) {
binding.setItem(item);
binding.executePendingBindings();
}
}
}
Bindingでリスナーをクリック
clickHandlerのインタフェースを作成する
public interface ClickHandler {
public void onButtonClick(View v);
}
レイアウトXML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler"
type="com.example.ClickHandler"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="click me"
android:onClick="@{handler.onButtonClick}"/>
</RelativeLayout>
</layout>
アクティビティのイベントを処理する
public class MainActivity extends Activity implements ClickHandler {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setHandler(this);
}
@Override
public void onButtonClick(View v) {
Toast.makeText(context,"Button clicked",Toast.LENGTH_LONG).show();
}
}
ラムダ式を使用したカスタムイベント
インタフェースの定義
public interface ClickHandler {
public void onButtonClick(User user);
}
Modelクラスを作成する
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
レイアウトXML
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="handler"
type="com.example.ClickHandler"/>
<variable
name="user"
type="com.example.User"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:onClick="@{() -> handler.onButtonClick(user)}"/>
</RelativeLayout>
</layout>
アクティビティコード:
public class MainActivity extends Activity implements ClickHandler {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setUser(new User("DataBinding User"));
binding.setHandler(this);
}
@Override
public void onButtonClick(User user) {
Toast.makeText(MainActivity.this,"Welcome " + user.getName(),Toast.LENGTH_LONG).show();
}
}
XMLコードでは使用できないがJavaコードで設定できるビューリスナーの場合、カスタムバインドでバインドできます。
カスタムクラス
public class BindingUtil {
@BindingAdapter({"bind:autoAdapter"})
public static void setAdapter(AutoCompleteTextView view, ArrayAdapter<String> pArrayAdapter) {
view.setAdapter(pArrayAdapter);
}
@BindingAdapter({"bind:onKeyListener"})
public static void setOnKeyListener(AutoCompleteTextView view , View.OnKeyListener pOnKeyListener)
{
view.setOnKeyListener(pOnKeyListener);
}
}
ハンドラクラス
public class Handler extends BaseObservable {
private ArrayAdapter<String> roleAdapter;
public ArrayAdapter<String> getRoleAdapter() {
return roleAdapter;
}
public void setRoleAdapter(ArrayAdapter<String> pRoleAdapter) {
roleAdapter = pRoleAdapter;
}
}
XML
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/tools" >
<data>
<variable
name="handler"
type="com.example.Handler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
bind:autoAdapter="@{handler.roleAdapter}" />
</LinearLayout>
</layout>
データバインディングのデフォルト値
プレビューペインには、データバインディング式のデフォルト値が表示されます。
例えば :
android:layout_height="@{@dimen/main_layout_height, default=wrap_content}"
これは設計時にwrap_content
をwrap_content
、プレビューウィンドウでwrap_content
として機能します。
もう一つの例は
android:text="@{user.name, default=`Preview Text`}"
プレビューウィンドウにPreview Text
が表示されますが、デバイス/エミュレータで実行すると、実際にバインドされたテキストが表示されます
カスタム変数によるデータバインディング(int、boolean)
時には、単一の値に基づいた表示/非表示のような基本的な操作を実行する必要があります。その単一の変数に対してモデルを作成できないか、モデルを作成するのは良い方法ではありません。 DataBindingは、これらのオペレーションを実行するための基本データ型をサポートします。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<variable
name="selected"
type="Boolean" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"
android:visibility="@{selected ? View.VISIBLE : View.GONE}" />
</RelativeLayout>
</layout>
javaクラスからその値を設定します。
binding.setSelected(true);
ダイアログでのデータバインディング
public void doSomething() {
DialogTestBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(context), R.layout.dialog_test, null, false);
Dialog dialog = new Dialog(context);
dialog.setContentView(binding.getRoot());
dialog.show();
}
BindingAdapterの参照としてウィジェットを渡す
レイアウトXML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="100dp"
app:imageUrl="@{url}"
app:progressbar="@{progressBar}"/>
</LinearLayout>
</layout>
BindingAdapterメソッド
@BindingAdapter({"imageUrl","progressbar"})
public static void loadImage(ImageView view, String imageUrl, ProgressBar progressBar){
Glide.with(view.getContext()).load(imageUrl)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
progressBar.setVisibility(View.GONE);
return false;
}
}).into(view);
}