KotlinでRealmとRecyclerViewを組み合わせて使ってみる


KotlinでRealmとRecyclerViewを使ってプロジェクトを作っていく上でいちいち調べなくてもいいようにするためのメモです。
記載しているバージョンについては適宜調整をお願いします。
ついでにこれからKotlin + Realmで導入を考えている方の助けになれば幸いです。

開発環境

・MacBook Pro 2017
・Android Studio v3.1.3
・Kotlin v1.2.60

KotlinでdataBindingを使えるように設定

appディレクトリ直下のbuild.gradleに以下を追加します。

apply plugin: 'kotlin-kapt'
dataBinding {
    enabled = true
}

RecyclerViewを使えるように設定

appディレクトリ直下のbuild.gradleに以下を追加します。

implementation 'com.android.support:recyclerview-v7:28.0.0-rc02'

Realmを使えるように設定

project直下のbuild.gradleに以下を追加します。

classpath "io.realm:realm-gradle-plugin:5.4.2"

appディレクトリ直下のbuild.gradleに以下を追加します。

apply plugin: 'realm-android'
implementation 'io.realm:android-adapters:2.1.1'

proguardの設定については以下の参照をお願いします。
https://realm.io/jp/docs/java/0.80.3/#proguard

以上で一度コンパイルをしてもらい、通れば問題ありません。

Realmを初期化するためにApplicationクラスを継承してCustomApplicationクラスを作成

ApplicationクラスのonCreate()メソッド内に以下を追加します。

Realm.init(this)
val realmConfiguration = RealmConfiguration.Builder().build()
Realm.deleteRealm(realmConfiguration) // Delete Realm between app restarts.
Realm.setDefaultConfiguration(realmConfiguration)

Realm.deleteRealm(realmConfiguration);この一行でデータを全て削除してしまうので、アプリの挙動に合わせて調整をお願いします。

Realmで使用するためのモデルクラスを用意する

こちらはKotlinファイルを用意して以下の1行だけになります。

open class Person(@PrimaryKey var id: Int? = null, var name: String? = null, var age: Int? = null, var gender: String? = null): RealmObject()

RecyclerViewを持ったActivityのレイアウトファイルを用意する

<?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">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

    </android.support.constraint.ConstraintLayout>
</layout>

RecyclerViewで使用するセルのレイアウトを用意する

LinearLayoutに対してidを設定しているのは背景色を設定して見やすくするためです。

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

    <LinearLayout
        android:id="@+id/cell_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/cell_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/cell_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/cell_gender"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>
</layout>

RecyclerViewに設定するadapterを用意する

Realmを簡単に使うため、RealmRecyclerViewAdapterを継承して使用しています。

class CustomRealmRecyclerViewAdapter(private val context: Context, private val collection: OrderedRealmCollection<Person>?, private val autoUpdate: Boolean)
    : RealmRecyclerViewAdapter<Person, CustomRealmRecyclerViewAdapter.CustomViewHolder>(collection, autoUpdate) {

    override fun getItemCount(): Int {
        return collection?.size ?: 0
    }

    override fun onCreateViewHolder(parent: ViewGroup, position: Int): CustomViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.view_cell, parent, false)
        return CustomViewHolder(DataBindingUtil.bind(view)!!)
    }

    override fun onBindViewHolder(viewHolder: CustomViewHolder, position: Int) {
        val person = collection?.get(position)
        viewHolder.binding.cellName.text = person?.name
        viewHolder.binding.cellAge.text = person?.age.toString()
        viewHolder.binding.cellGender.text = person?.gender
        viewHolder.binding.cellLayout.setBackgroundColor(if (position % 2 == 0) Color.LTGRAY else Color.WHITE)
    }

    class CustomViewHolder(val binding: ViewCellBinding): RecyclerView.ViewHolder(binding.root)
}

RecyclerViewにadapterを設定する

onCreate内でこれから必要なデータを作成する都合でonStart()メソッド内でadapterの設定を行っています。
アプリに合わせた修正を適宜お願いします。

override fun onStart() {
    super.onStart()
    val realmResults = mRealm.where(Person::class.java).findAll()
    mBinding.recyclerView.adapter = CustomRealmRecyclerViewAdapter(applicationContext, realmResults, false)
}

アダプターに流し込むデータを用意する

とりあえずのデータを100件用意しています。onCreate()メソッド内でデータの作成を行っていますが、実際に使う場合ではretrofit等でサーバーからデータを取得した後にデータの保存処理は行うと思われます。

mRealm = Realm.getDefaultInstance()
mRealm.executeTransaction{
    val list = mutableListOf<Person>()
    for (i in 0..99) {
        list.add(Person(i, "ほげほげ:" + i.toString(), i, if (i % 2 == 0) "男" else "女"))
    }
    it.copyToRealmOrUpdate(list)
}

Realmの後処理をする

override fun onDestroy() {
    super.onDestroy()
    mRealm.close()
}

AndroidManifestにCustomApplication使うように指定する

<application
    android:name=".application.CustomApplication" ←追加する
以上で実行して問題なければ以下のように表示されると思います。