くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

DataBindingを使ってみる

listviewの表示をDataBindigを使ってみることにしました。 もともと、android.R.layout.simple_expandable_list_item_2というオープンソースを使っていたのですが、カスタマイズできないのと、カスタマイズするならDataBindingを使ってみようと思ったのが、キッカケです。

①build.gradleに設定の追加

まずはDataBindigを使えるように設定を追加します。 build.gradleに以下を追加します。

// 追加
apply plugin: 'kotlin-kapt'

android {
    // 追加
    dataBinding {
        enabled = true
    }
}

②レイアウトの作成

次にレイアウトの作成を行います。

task_item.xml

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

    <data>
        <variable
            name="taskDetail"
            type="com.kumaydevelop.todoreminder.Model.TaskDetail" />
    </data>

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{taskDetail.title}"/>

        <TextView
            android:id="@+id/limit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{taskDetail.limit}"/>
    </LinearLayout>
</layout>

手順としては、最初のタグをLayoutタグで括り、dataタグを追加します。

variable属性のnameが今回の場合はtext属性を表示するために使われています。

今回は省略しますが、variable属性のtypeがモデルとなります。

③Adapterの作成

次にAdapterを作成します。普通にlistViewを作成する場合でも作るのですが、少し異なります。

TaskAdapter

class TaskAdapter(val context: Context) : BaseAdapter() {

    var tasks: List<TaskDetail> = emptyList()

    override fun getCount(): Int {
        return tasks.size
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getItem(position: Int): Any {
        return tasks[position]
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        var binding: TaskItemBinding?

        if (convertView == null) {
            // カスタム作成したtask_item.xmlを使う
            binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.task_item,parent,false)
            binding.root.tag = binding
        } else {
            // 今まで使っていたものを使いまわす
            binding = convertView.tag as TaskItemBinding
        }

        binding!!.taskDetail = getItem(position) as TaskDetail
        // root化してviewを返す
        return binding.root
    }
}

ここで使われている、TaskItemBindingは②で作成したxmlがあれば使えるはずです。 もしくはビルドしてください。

今回はtask_item.xmlなので、xmlを除いた末尾からの名前で作成されるはずです。

流れとしては下記になります。

①getViewでconvertViewがnullの時にBookItemBindingクラスを作成。それ以外の時は使い回す

②getViewの戻りはBookItemBindingのインスタンスのrootすることでViewが返る

viewHolderが必要なくなりましたね! こちらの方が直感的に見やすい印象があります。

④Activityとの連携

最後に③で作ったものをActivityと連携させます。

今回はActivityもDataBindingを使っています。

activity_main.xml

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

    <data>
        <variable
            name="onItemClick"
            type="android.widget.AdapterView.OnItemClickListener" />
    </data>

    <android.support.design.widget.CoordinatorLayout
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".Activity.MainActivity">

        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:onItemClickListener="@{onItemClick}"/>

    </android.support.design.widget.CoordinatorLayout>

</layout>

次にMainActivityで連携させます。

MainActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        val binding : ActivityMainBinding

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        val listAdapter = TaskAdapter(applicationContext)

        // 省略していますが、taskListは表示させるデータのリストです。
        listAdapter.tasks = taskList
        binding.listview.adapter = listAdapter

    }

ここは、DataBindingを使わなくでもあまり変わらないと思います。

違うのが、DataBindingUtilを使って、Bidingクラスを取得してxmlで定義してある必要なデータをセットするところと、binding.listview.adapter = listAdapterでActivityMainBindingのlistViewにAdapterをセットしているところです。

実際に使ってみて、web開発の経験がある人は画面に変数をセットするのはなじみがあって、導入しやすい気がしました。

参照

Kotlin × Data BindingでonItemClickListener付きListViewアプリを作る