[Android] realm-android-adaptersを使ってRecyclerViewを実装する

2019年現在、Androidアプリ開発のデータベースはRealmを使うことが多いと思います。Realmの用意しているrealm-android-adaptersというライブラリを使うと、RecyclerViewの更新処理を自動で行ってくれて、とてもいい感じです。

realm-android-adaptersを使うには、プロジェクトでRealmやRecyclerViewを使えるようにした状態で、app/build.gradlewに以下の記述を追加します。追加したら、忘れずにsyncしておきます。

dependencies {
    // Realm Android Adapters
    implementation 'io.realm:android-adapters:3.1.0'
}

これでrealm-android-adaptersを使えるようになりました。次に、RecyclerViewの1行分のデータを、RealmObjectの子クラスとして実装します。今回はシンプルに以下のような実装としました。これをRecyclerViewの1行1行に表示していきます。

open class Task(@PrimaryKey open var id: Int = 1,
                @Required open var text: String = ""): RealmObject()

まずは上記のTaskクラスをRealmに保存しましょう。特に特別なことはなく、普通にRealmに保存してあげればOKです。

val realm = Realm.getDefaultInstance()
realm.executeTransaction { r ->
    val task = Task(1, projectName)
    r.copyToRealmOrUpdate(task)
}
realm.close()

TaskクラスをRealmから読み出します。こちらも特に特別なことはなく、普通にRealmから読みだしてあげればOKです。

val realm = Realm.getDefaultInstance()
val tasks = realm.where(Task::class.java).findAll()

次に、RecyclerViewを実装していきます。まずはAdapterを実装し、次にViewHolderを実装します。Adapterは、RealmRecyclerViewAdapterを継承します。少し長いですが、Adapterのコードを全て示します。基本的には、RealmRecyclerViewAdapterを継承しているというだけで、特別なことはありません。

class MainAdapter(private val context: Context,
                  private val itemClickListener: MainViewHolder.ItemClickListener,
                  private val tasks: OrderedRealmCollection<Task>,
                  autoUpdate: Boolean): RealmRecyclerViewAdapter<Task, MainViewHolder>(tasks, autoUpdate) {
    private var recyclerView: RecyclerView? = null

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        super.onAttachedToRecyclerView(recyclerView)
        this.recyclerView = recyclerView
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        super.onDetachedFromRecyclerView(recyclerView)
        this.recyclerView = null
    }

    override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
        holder.titleTextView.text = tasks[position].text
    }

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

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
        val layoutInflater = LayoutInflater.from(context)
        val view = layoutInflater.inflate(R.layout.item_main, parent, false)

        view.setOnClickListener { _ ->
            recyclerView?.let {
                itemClickListener.onItemClick(view, it.getChildAdapterPosition(view))
            }
        }

        return MainViewHolder(view)
    }
}

ViewHolderについては省略してもいいんですが、一応、コードを示しておきます。特別なことは何もない、普通のViewHolderです。

class MainViewHolder(view: View): RecyclerView.ViewHolder(view) {

    interface ItemClickListener {
        fun onItemClick(view: View, position: Int)
    }

    val titleTextView: TextView = view.titleTextView
}

ここまできたら、recyclerViewの表示を行います。AdapterにRealmから取得したOrderedRealmCollectionを渡してあげればOKです。こちらも、Fragmentのコードを全て示します。

class MainFragment: Fragment(), MainViewHolder.ItemClickListener {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val context = context ?: return

        val realm = Realm.getDefaultInstance()
        val tasks = realm.where(Task::class.java).findAll()

        recyclerView.apply {
            adapter = MainAdapter(context, this@MainFragment, tasks, true)
            layoutManager = LinearLayoutManager(context)
            setHasFixedSize(true)
        }
    }

    override fun onItemClick(view: View, position: Int) {
        // do something here
    }
}

以上です。これで、RealmにTaskが追加/更新されたら、RecyclerViewも自動で追加/更新されるようになりました。

参考文献