今更遅れてDataBinding事始め(3.7)
はじめに
を読んでのエントリ
これStackOveFlowとかには載ってて、ちょっと記載方法を書いてみる
コンパイルエラーを発生させない書き方
ViewDataBinding のまま使えという話
layout/recycler_item.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <data> <variable name="dto" type="hoge.fuga.itemDto" /> </data> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="warp_content"> <ImageView android:gravity="center_vertical|center_horizontal" android:layout_weight="0" android:layout_width="64dp" android:layout_height="64dp" app:imageUrl="@{dto.iamge_url}" /> <TextView android:gravity="center_vertical|center_horizontal" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_content" android:text="@{dto.title}"/> </LinearLayout> </layout>
- RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ItemViewHolder> { private ArrayList<ItemDto> mItemList; // ViewHolder public static class ItemViewHolder extends RecyclerView.ViewHolder { private ViewDataBinding mBinding; public ItemViewHolder(View v) { super(v); // Bind処理 mBinding = DataBindingUtil.bind(v); } public ViewDataBinding getBinding() { return mBinding; } } public RecyclerViewAdapter(ArrayList<ItemDto> itemList) { mItemList = itemList; } @Override public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // recycler_itemレイアウト View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, parent, false); return new ItemViewHolder(v); } @Override public void onBindViewHolder(ItemViewHolder holder, int position) { ItemDto dto = mItemList.get(position); //variable は hoge.fuga.BR にマッピングされているのでBRタグに値を設定する holder.getBinding().setVariable(BR.dto, dto); holder.getBinding().executePendingBindings();//即時反映をお願いする } //== レイアウトの単純記述で頑張れない奴 === // // BindingAdapterとかで頑張れ @BindingAdapter({"imageUrl"}) //★ public static void loadImage(ImageView view, String imageUrl) { Picasso.with(view.getContext()) .load(imageUrl) .placeholder(R.drawable.placeholder) .into(view); } }
BindingAdapter アノテーション
- staticメソッド
- 第一引数に対象となるViewをとること
- 第二引数以降に属性値を取ること(各引数ごとに、一個の属性が必要)
- requireAll = false を定義すると引数が足りなくても関数が呼び出されるようになる(デフォルトはtrue)
@BindingAdapter({"imageUrl"}) //★ public static void loadImage(ImageView view, String imageUrl) { Picasso.with(view.getContext()) .load(imageUrl) .placeholder(R.drawable.placeholder) .into(view); } @BindingAdapter({"imageUrl2", "error"}) //@BindingAdapter({"imageUrl2", "error"}, requireAll = false) public static void loadImageWithError(ImageView v, String imageUrl, Drawable error) { Glide.with(v.getContext()).load(imageUrl).error(error).into(v); }
BindAdapterのnamespaceに関して
@BindingAdapter({“bind:imageUrl”}) //★
な書き方をしている記事がありますが、android gradle plugin 2.2.2の時点だと
namespaceがついてると文法エラー扱いになる?感じも*1
ImageViewにたいして
正直ListViewサイズなら大きめの画像はいらないので
Glide.with(v.getContext()).load(imageUrl).thumbnail(0.1f).into(v)
辺りのサムネイル取得ほうがいいかもしれない。
レイアウトピッタシの大きめの画像を設定するなら
<ImageView android:scaleType="fitStart" android:layout_width="match_parent" android:layout_height="warp_content" app:imageUrl="@{dto.iamge_url}" />
辺りの指定をxml側ですべきなのかなとは思ったりも。
data-binding有効時の 定義済みの情報に関して
基本的に先定義優先なので、下記のあたりの設定走っておくべき知識らしい
したがって、android:XXX を上書きな黒魔術はできないわけで・・(苦笑
その他のアノテーション
BindingMethod アノテーション
使っているサンプルをあまり見たことなんだよな。。
- ViewBindingAdapter.java
@BindingMethods({ @BindingMethod(type = View.class, attribute = "android:onClick", method = "setOnClickListener") }) public class ViewBindingAdapter { }
のソースを見ましょうな解説サイトが結構多め
InverseBindingAdapter アノテーション
実は、双方向Bindingの中で動いている処理らしい(View => Java)
//View => Java @InverseBindingAdapter(attribute = "color") public static int getColorInt(ColorPicker view) { return ConvertColorEnumToInt(view.getColor()); } //View <= Java @BindingAdapter("color") public static void setColor(ColorPicker view, int color) { if (color != view.getColor()) { view.setColor(color); } }
- 参考資料
*1:ビルドが失敗する or ビルドが成功してもview側からみつけられない挙動