今更遅れてDataBinding事始め(3.7)

はじめに

を読んでのエントリ

これStackOveFlowとかには載ってて、ちょっと記載方法を書いてみる

コンパイルエラーを発生させない書き方

  • ViewDataBinding のまま使えという話

    • ただそのクラス特有のidとかsetterとかは引っ張ってこれない
    • viewのlayout.xml上で頑張れという方針
    • 難しいのはBindAdapterアノテーションで拡張すればいいよね? らしい
  • 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側からみつけられない挙動