【Android】画面コンポーネント / ListView ~ Realm データを表示 ~

 ■ はじめに

https://dk521123.hatenablog.com/entry/2013/10/10/005010

の続き。今回は、ListView を使用する。(特にアダプタ周り)

■ 初期設定

例1での準備

build.gradle (Project:XXXX)

buildscript {

    dependencies {
        classpath 'com.android.tools.build:gradle:4.0.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.realm:realm-gradle-plugin:6.0.2"
    }
}

build.gradle (Module:app)

apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'

dependencies {
    // RealmBaseAdapter 使用時
    implementation 'io.realm:android-adapters:2.1.1'
}

https://realm.io/docs/java/latest/#adapters

例2:Ankoを用いる場合

 * Realm (DB) と Anko (ページ移動) を追加する
 => Realm と Anko については、以下の関連記事を参照のこと

./build.gradle

buildscript {
    ext.kotlin_version = '1.2.50'
    ext.anko_version = '0.10.5' // ★ Add ★
    // ... 略 ...
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.4'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.realm:realm-gradle-plugin:5.4.1" // ★ Add ★
    }

./app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' // ★ Add ★
apply plugin: 'realm-android' // ★ Add ★

// ... 略 ...

dependencies {
    // ... 略 ...
    implementation "org.jetbrains.anko:anko:$anko_version" // ★ Add ★
    implementation 'io.realm:android-adapters:2.1.1' // ★ Add:今回で初めて追加 ★

 ■ サンプル

例1:Kotlin

[1] ListView を追加し、プロパティを変更する
 + id : 任意の文字列(今回は「wordListView」)
 + padding : 16dp
[2] ListView の各行のレイアウトを追加するために
 [app]-[res]-[layout] を選択し、
 右クリックし、[New]-[Layout Resource File]を選択し
 以下を入力し「OK」ボタン押下
 + File Name : 任意の文字列(今回は「word_row.xml」)
 + Root element : TextView
[3] [app]-[res]-[layout] 配下にある[2]のファイルをダブルクリックし
 以下のプロパティを入力する
 + id : 任意の文字列(今回は「wordItem」)

EnglishWord.kt

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.annotations.Required
import java.util.*

open class EnglishWord : RealmObject() {
    @PrimaryKey
    var id: String = UUID.randomUUID().toString()
    @Required
    var english: String = ""
    var japanese: String = ""
    var memo: String = ""
}

MainAdapter.kt

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.Toast
import io.realm.Realm
import io.realm.RealmResults
import io.realm.Sort
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private lateinit var realm: Realm

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

        Realm.init(this)
        realm = Realm.getDefaultInstance()

        addFloatingActionButton.setOnClickListener {
            val intentToMoveAddPage = Intent(
                this, AddActivity::class.java)
            startActivity(intentToMoveAddPage)
        }

        // ListView の Clickイベント
        wordListView.setOnItemClickListener {
                adapterView, _, position, _ ->
            val listViewValue = adapterView.getItemAtPosition(position) as String
            Toast.makeText(this, "$listViewValue", Toast.LENGTH_SHORT).show()
        }
    }

    // 再開時の呼び出されるイベント
    override fun onResume() {
        super.onResume()

        val englishWordList = readAll()

        wordListView.adapter = ArrayAdapter<String>(
            this@MainActivity,
            R.layout.word_row,
            englishWordList.map { "${it.english}:\n${it.japanese}" }
        )
    }

    fun readAll(): List<EnglishWord> {
        return realm.copyFromRealm(
            realm.where(EnglishWord::class.java)
                .findAll()
                .sort("english", Sort.ASCENDING))
    }
}

AddActivity.kt

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.activity_add.*
import java.util.*

class AddActivity : AppCompatActivity() {
    private lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add)

        Realm.init(this)
        realm = Realm.getDefaultInstance()


        // Add
        addButton.setOnClickListener {
            try {
                realm.executeTransaction {
                    val englishWord =
                        realm.createObject(EnglishWord::class.java!!,
                            UUID.randomUUID().toString())
                    englishWord.english = enEditText.text.toString()
                    englishWord.japanese = jpEditText.text.toString()
                    englishWord.memo = memoEditText.text.toString()


                    val intentToMoveMainPage = Intent(
                        this, MainActivity::class.java)
                    startActivity(intentToMoveMainPage)
                }
            } catch (ex: Exception) {
                Toast.makeText(this, "Error...", Toast.LENGTH_SHORT).show()
                ex.printStackTrace()
            }
        }
    }
}

例2:Ankoを用いる場合

をベースにする。
「DemoApplication.kt」「AndroidManifest.xml」「Comment.kt」は、
同じなので、上記の関連記事を参照。

CommentListViewAdapter.kt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import io.realm.OrderedRealmCollection
import io.realm.RealmBaseAdapter

class CommentListViewAdapter(data: OrderedRealmCollection<Comment>?) : RealmBaseAdapter<Comment>(data) {

    inner class ViewHolder(cell: View) {
        val id = cell.findViewById<TextView>(android.R.id.text1)
        val title= cell.findViewById<TextView>(android.R.id.text2)
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view: View
        val viewHolder: ViewHolder

        when (convertView) {
            null -> {
                val layoutInflater = LayoutInflater.from(parent?.context)
                view = layoutInflater.inflate(android.R.layout.simple_list_item_2,
                        parent, false)
                viewHolder = this.ViewHolder(view)
                view.tag = viewHolder
            }
            else -> {
                view = convertView
                viewHolder = view.tag as ViewHolder
            }

        }
        adapterData?.run {
            val comment = get(position)
            viewHolder.id.text = comment.id.toString()
            viewHolder.title.text = comment.title
        }
        return view
    }
}

 ListViewActivity.kt

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import io.realm.Realm
import kotlinx.android.synthetic.main.activity_list_view.*
import org.jetbrains.anko.startActivity

class ListViewActivity : AppCompatActivity() {
    private lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_list_view)

        this.realm = Realm.getDefaultInstance()
        val comments = realm.where(Comment::class.java).findAll().sort("id", Sort.ASCENDING)
        this.listView.adapter = CommentListViewAdapter(comments)
        this.listView.setOnItemClickListener { parent, view, position, id ->
            val comment = parent.getItemAtPosition(position) as Comment
            this.startActivity<EditCommentActivity>("comment_id" to comment.id)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        this.realm.close()
    }
}

 EditCommentActivity.kt

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.activity_edit_comment.*

class EditCommentActivity : AppCompatActivity() {
    private lateinit var realm: Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        this.setContentView(R.layout.activity_edit_comment)

        this.realm = Realm.getDefaultInstance()

        val commentId = intent?.getLongExtra("comment_id", -1L)
        if (commentId != -1L) {
            val comment = realm.where<Comment>()
                    .equalTo("id", commentId).findFirst()
            this.resultIdTextView.text = comment?.id.toString()
            this.resultTitleEditText.setText(comment?.title)
            this.resultDetailEditText.setText(comment?.detail)
            this.deleteButton.visibility = View.VISIBLE
        } else {
            this.deleteButton.visibility = View.INVISIBLE
        }

        this.saveButton.setOnClickListener { it: View? ->
            when (commentId) {
                -1L -> {
                    try {
                        realm.executeTransaction {
                            val maxId = realm.where<Comment>().max("id")
                            val targetId = (maxId?.toLong() ?: 0L) + 1L
                            val comment = realm.createObject<Comment>(targetId)
                            comment.title = this.resultTitleEditText.text.toString()
                            comment.detail = this.resultDetailEditText.text.toString()
                            Toast.makeText(this, "追加しました. ID : " + targetId.toString(),
                                    Toast.LENGTH_SHORT).show()
                        }
                    } catch (ex: Exception) {
                        ex.printStackTrace()
                    }
                }
                else -> {
                    try {
                        realm.executeTransaction {
                             val comment = realm.where<Comment>()
                                    .equalTo("id", commentId).findFirst()
                            comment?.title = resultTitleEditText.text.toString()
                            comment?.detail = resultDetailEditText.text.toString()
                            Toast.makeText(this, "修正しました.",
                                    Toast.LENGTH_SHORT).show()
                        }
                    } catch (ex: Exception) {
                        ex.printStackTrace()
                    }
                }
            }
        }
        this.deleteButton.setOnClickListener {
            realm.executeTransaction {
                realm.where<Comment>().equalTo("id", commentId)?.findFirst()?.deleteFromRealm()
            Toast.makeText(this, "削除しました.",
                        Toast.LENGTH_SHORT).show()
            }
        }
    }

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

参考文献

https://qiita.com/orimomo/items/bc1f2065f44104176f7f

関連記事

画面コンポーネント / ListView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2013/10/10/005010
画面コンポーネント / RecyclerView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/07/21/000000
DB を使用する ~ Realm 編 ~
https://dk521123.hatenablog.com/entry/2018/09/01/213649
Kotlin / Realm で英単語帳を作る
https://dk521123.hatenablog.com/entry/2020/07/20/232009
Realm に関するトラブル
https://dk521123.hatenablog.com/entry/2020/07/24/000000