LoginSignup
21
24

More than 5 years have passed since last update.

Android DataBinding 〜ListViewで使う〜

Last updated at Posted at 2017-04-20

はじめに

リスト表示でDataBindingを使う方法についてまとめます。
ListViewで一覧を表示するだけでなく、追加や削除した結果を動的に反映する方法も紹介します。

Step0. 準備

例として、日時の文字列の一覧を表示するサンプルを作ります。

生成されたときの日時を文字列で表示します。

DateTime.java
public class DateTime {
    private String dateTime;

    public DateTime() {
        this.dateTime = new Date().toString();
    }

    public String getName() {
        return dateTime;
    }
}
view_row_date_time_list.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="dateTime" type="com.example.databindinglistview.DateTime" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentStart="true"
            android:text="@{dateTime.name}"
            />
    </RelativeLayout>
</layout>

Step1. Adapter作成

リストの子要素を管理するAdapterを作成します。
Viewとの紐づけにDataBindingを用いることでViewHolderが不要になり、以下のようにかなりスッキリ書けます。

DateTimeAdapter.java
public class DateTimeAdapter extends ArrayAdapter<DateTime>{

    public DateTimeAdapter(Context context, ArrayList<DateTime> dateTimeList) {
        super(context, 0, dateTimeList);
    }

    @NonNull
    @Override
    public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
        ViewRowDateTimeListBinding binding;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(getContext());
            binding = DataBindingUtil.inflate(inflater, R.layout.view_row_date_time_list, parent, false);

            convertView = binding.getRoot();
            convertView.setTag(binding);
        } else {
            binding = (ViewRowDateTimeListBinding) convertView.getTag();
        }

        binding.setDateTime(getItem(position));

        return binding.getRoot();
    }
}

Step2. リストデータをセットする

xml上でListを渡すために、ListViewを拡張します。
setList(...)を新たに追加して、中でListからAdapterを作っているのですが、このsetListは自動セッターという仕組みでxmlからlist属性として見えるようになります。
https://developer.android.com/topic/libraries/data-binding/index.html?hl=ja#automatic_setters

DateTimeListView.java
public class DateTimeListView extends ListView{

    DateTimeAdapter adapter;

    public DateTimeListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setList(List<DateTime> dateTimeList){
        Log.d("DEBUG", "setList is called");

        if (getAdapter() == null) {
            adapter = new DateTimeAdapter(getContext(), dateTimeList);
            setAdapter(adapter);
        }
    }
}

(参考)ListViewをいじらず、BindingAdapterでカスタムセッターを用意する手段もアリです。

DataBindingAdapters.java
@BindingAdapter("list")
public static void setList(ListView listView, List<DateTime> dateTimeList) {
    Log.d("DEBUG", "setList is called");

    if (listView.getAdapter() == null) {
        DateTimeAdapter adapter = new DateTimeAdapter(listView.getContext(), dateTimeList);
        listView.setAdapter(adapter);
    }
}

拡張したListViewをActivityのレイアウトに配置して、先程定義したlist属性にリストデータを入れます。

activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <import type="java.util.List" />
        <import type="com.example.databindinglistview.DateTime"/>
        <variable name="dateTimeList" type="List&lt;DateTime&gt;" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >

        <com.example.databindinglistview.DateTimeListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fadeScrollbars="false"
            app:list="@{dateTimeList}"
            />
    </LinearLayout>
</layout>

DataBindingでList<>を渡すときは、エスケープシーケンスを使ってList&lt;DateTime&gt;のように書きます。
あとは、ActivityでBindingにデータをセットすればListViewに表示されます。

MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    List<DateTime> dateTimeList = new ArrayList<>();
    dateTimeList.add(new DateTime());
    dateTimeList.add(new DateTime());
    dateTimeList.add(new DateTime());

    binding.setDateTimeList(dateTimeList);
}

Step3. アイテムのクリックイベントを拾う

イベントを受け取るHandlerを用意します。
引数はAdapterView.OnItemClickListeneronItemClickに合わせておきます。

DataBindingAdapters.java
public interface MainEventHandler {
    void onItemClick(AdapterView<?> parent, View view, int position, long id);
}

あとはListViewで以下のようにセットすれば、子要素をクリックしたときのイベントを受け取ることができます。

activity_main.xml
<data>
  ...
  <variable name="handlers" type="MainEventHandler" />
</data>

...
<com.example.databindinglistview.DateTimeListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fadeScrollbars="false"
    app:list="@{dateTimeList}"
    android:onItemClickListener="@{handlers.onItemClick}"
    />

Step4. リストの追加や削除を動的に反映させる

上記で単にリストに要素を表示するだけのものができました。
しかし、このままではリストに追加や削除があっても反映されません。

そこで今回は、リストの変更を通知できるObservableArrayListを使ってみることにしました。
Adapterを直接触らずにListViewを更新できるようになるので便利です。

MainActivity.java
//List<DateTime> dateTimeList = new ArrayList<>();
ObservableArrayList<DateTime> dateTimeList = new ObservableArrayList<>();

ほか、関連する部分もList<DateTime>からObservableArrayList<DateTime>に書き換えます。
あとは、ListViewでlist更新時にnotifyDataSetChanged()を呼んでおきます。

DateTimeListView.java
public void setList(List<DateTime> dateTimeList){
  ...
  adapter.notifyDataSetChanged();
}

長くなるのでソースは割愛しますが、この置き換えだけでリストの追加削除時にDataBindingが変更を検知して、setList(...)notifyDataSetChanged()が呼ばれ、ListViewの表示が直ちに更新されるようになります。

リストの追加削除を含むソース全体はこちら

おわりに

ListViewでDataBindingを使うとAdapter周りのソースをかなりシンプルにすることができました。
さらに要素をObservableArrayList<>にすることで、元のデータを操作するだけでListViewが自動的に更新されるようになり、呼び出し側はAdapterさえ触らずに済むようにできました。

21
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
24