techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

Data Bindingを使ってみる その5

今回は、Data BindingをRecycler Viewで使用してみます。サンプルとしては、TextViewを一つ持ったレイアウトをリスト形式に並べて表示してみるようにします。
RecyclerViewはAndroid 5.1 Lollipopから追加されたUIコンポーネントで、その名の通りViewをリサイクルして使い回しつつレイアウトすることでリストやグリッドが作れたりします。
ここでは、DataBindingを用いて、RecyclerViewで使い回すTextView一つ一つに値をセットしていきます。

実行結果
f:id:uentseit:20160407232723p:plain

なお、今回はData Bindingの使い方の説明がメインなので、Recycler Viewの使い方に関する詳しい説明はまたどこかの機会で。

RecyclerViewでリストを構築

まずは普通にRecyclerViewでリストを構築してみます。
今回RecyclerViewで使い回されるViewは、以下に示すrecycler_item.xmlになります。
[recycler_item.xml]

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/layout_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/txt_weapon_name"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:layout_margin="@dimen/text_margin"
        ></TextView>
    </LinearLayout>

Adapterも作成します。
onBindViewHolderメソッド内の処理で、recycler_item内のTextViewに文字列(Weapon#name)をセットしています。
後ほど、この部分をDataBindingで置き換えます。
[MyItemRecyclerViewAdapter.java]

import techium.hatenablog.com.monhaan1.databinding.RecyclerItemBinding;

public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {

    private List<Weapon> mWeapons;

    public MyItemRecyclerViewAdapter(List<Weapon> items) {
        mWeapons = items;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {

        final Weapon item = mWeapons.get(position);

        holder.mTextView.setText(item.getName());
        holder.mView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Toast.makeText(v.getContext(),item.name.get(), Toast.LENGTH_SHORT).show();
                Toast.makeText(v.getContext(),item.getName(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mWeapons.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private TextView mTextView;
        private View mView;

        public ViewHolder(View view) {
            super(view);
            mView = view;
            mTextView = (TextView)view.findViewById(R.id.txt_weapon_name);
        }
    }
}

Activityは以下の通りです。AdapterをRecyclerViewにセットするのが役割です。
activity_recycler_listに対してDataBindingを用いていますが、そこはあまり本題とは関係ないので無視してください。
[RecyclerListActivity.java]

import techium.hatenablog.com.monhaan1.databinding.ActivityRecyclerListBinding;

public class RecyclerListActivity extends AppCompatActivity {

    private List<Weapon> mWeapons;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mWeapons = new ArrayList<>();
        mWeapons.add(new Weapon("装備なし"));
        mWeapons.add(new Weapon("大剣"));
        mWeapons.add(new Weapon("ハンマー"));
        MyItemRecyclerViewAdapter adapter = new MyItemRecyclerViewAdapter(mWeapons);

        ActivityRecyclerListBinding binder = DataBindingUtil.setContentView(this, R.layout.activity_recycler_list);
        binder.recyclerList.setLayoutManager(new LinearLayoutManager(this));
        binder.recyclerList.setAdapter(adapter);
    }
}

Activityのレイアウトです。RecyclerViewを一つ持っています。
[activity_recycler_list.xml]

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <android.support.v7.widget.RecyclerView
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/recycler_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"/>

    </RelativeLayout>
</layout>

DataBindingで置き換え

それでは、DataBindingを使用すると処理がどう変わるのか見てみます。

まず、recycler_itemのレイアウトをlayoutタグで囲い、Weaponクラスのオブジェクトをdataタグ内のvariableタグに定義することで、データをバインドできるようにします。TextViewには、Weapon#nameをテキストとしてセットします。(このあたりは、その1を見ていただくとわかると思います。) [recycler_item.xml]

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <data>
        <variable name="weapon" type="techium.hatenablog.com.monhaan1.Weapon"/>
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/layout_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/txt_weapon_name"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:layout_margin="@dimen/text_margin"
            android:text="@{weapon.name}"></TextView>
    </LinearLayout>
</layout>

次に、MyItemRecyclerViewAdapterを変更していきます。 [recycler_itemのTextViewに値をセットしているonBindViewHolderと、ViewHolderが主な変更点です。 MyItemRecyclerViewAdapter.java]

import techium.hatenablog.com.monhaan1.databinding.RecyclerItemBinding;

public class MyItemRecyclerViewAdapter extends RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder> {

    private List<Weapon> mWeapons;

    public MyItemRecyclerViewAdapter(List<Weapon> items) {
        mWeapons = items;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.recycler_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        final Weapon item = mWeapons.get(position);

        holder.getBinding().setVariable(techium.hatenablog.com.monhaan1.BR.weapon, item);
        holder.getBinding().executePendingBindings();
        holder.getBinding().getRoot().setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(v.getContext(),item.name.get(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mWeapons.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private RecyclerItemBinding mBinding;

        public ViewHolder(View view) {
            super(view);
            mBinding = DataBindingUtil.bind(itemView);  // 42行め
        }

        public ViewDataBinding getBinding() {
            return mBinding;
        }

    }
}

まずはViewHolderクラスにRecyclerItemBindingクラスのインスタンスを定義します。RecyclerItemBindingクラスは、recycler_itemをlayoutタグで囲ったことで自動生成されたクラスで、データがバインドされたことを検知するためのリスナーをセットするためにBaseObservableクラスを継承しています。 42行目で、recycler_itemにバインドするためのバインダーとして利用できるようにします。 executePendingBindingsを呼び出すことで、バインドを直ちに実行しています。

onBindViewHolderでは、ViewHolderからバインダーを取得し、setVariableメソッドを使用してデータをバインドしています。 第1引数では、バインドしたいオブジェクトの"BR id"を指定します。BR idは、recycler_itemのvariableタグにて"weapon"を定義したことで自動生成されています。 第2引数で、実際にバインドしたいオブジェクトを指定しています。 これで、recycler_itemにweaponオブジェクトがバインドされ、weaponオブジェクトに格納されていたnameプロパティの値でTextViewが表示されます。

ちなみに、RecyclerViewは ListViewとは異なり、項目が選択された時のイベントがデフォルトでは存在しないため、24行目で自分でリスナーをセットしています。

[Weapon.java]

public class Weapon  {
    public final ObservableField<String> name = new ObservableField<>();

    public Weapon(String s) {
        name.set(s);
    }
}

ObservableFieldについては、前回の記事を参考にしてください。

RecyclerListActivity.java ・・・変更なし

activity_recycler_list.xml ・・・変更なし

実行結果は冒頭の画像になります。

さて、長くなりましたが、今日はこんなところです。