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開発の経験がある人は画面に変数をセットするのはなじみがあって、導入しやすい気がしました。