【Android】Kotlin / Realm で英単語帳を作る

■ はじめに

Android の勉強を再開して、いつの間にか
Ankoが非推奨になったりと、色々変化があった。
とりあえず、Anko抜きで、簡単なアプリとして
英単語帳でも作ってみる

補足:Github

* つらつらと、以下の Github で全コードを置いておく

https://github.com/dk521123/EnglishCards

■ サンプル

準備

以下を追加し、
Android Studioの[Build]-[Rebuild Project]を選択し、ビルドする

build.gradle (Project:Xxxx)

dependencies {
     classpath "io.realm:realm-gradle-plugin:6.0.2"
}

build.gradle (Module:app)

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

dependencies {
    implementation 'com.google.android.material:material:1.2.0-rc01'
    implementation 'io.realm:android-adapters:2.1.1'
}

デザイン部

* 画面は2つ。
 + MainActivity.kt ... ListView / FloatingActionButton を追加
 + EditActivity.kt ... EditText x 3 / Button x 2 を追加

プログラム部

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 = ""
}

MainActivity.kt

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
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, EditActivity::class.java)
            startActivity(intentToMoveAddPage)
        }

        // ListView の Clickイベント
        wordListView.setOnItemClickListener {
                parent, view, position, id ->
            val englishWord =
                parent.getItemAtPosition(position) as EnglishWord
            val intentToMoveEditPage = Intent(
                this, EditActivity::class.java)
            intentToMoveEditPage.putExtra("english_word_id", englishWord.id)
            startActivity(intentToMoveEditPage)
        }
    }

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

        val englishWordList = readAll()
        wordListView.adapter = EnglishWordAdapter(englishWordList)
    }

    fun readAll(): RealmResults<EnglishWord> {
        val returnValues =
            realm.where(EnglishWord::class.java)
                .findAll()
                .sort("english", Sort.ASCENDING)
        return returnValues
    }
}

EditActivity.kt

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Toast
import io.realm.Realm
import kotlinx.android.synthetic.main.activity_edit.*
import java.util.*

class EditActivity : AppCompatActivity() {
    private lateinit var realm: Realm
    private lateinit var englishWordId: String

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

        realm = Realm.getDefaultInstance()

        val englishWordId =
            intent.getStringExtra("english_word_id")
        if (englishWordId != null) {
            this.englishWordId = englishWordId
            val englishWord =
                realm.where(EnglishWord::class.java)
                    .equalTo("id", this.englishWordId)
                    .findFirst()
            enEditText.setText(englishWord?.english)
            jpEditText.setText(englishWord?.japanese)
            memoEditText.setText(englishWord?.memo)

            deleteButton.visibility = View.VISIBLE
        } else {
            enEditText.setText("")
            jpEditText.setText("")
            memoEditText.setText("")

            deleteButton.visibility = View.INVISIBLE
        }

        // 更新処理
        saveButton.setOnClickListener {
            when(englishWordId) {
                null -> {
                    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()
                    }
                }
                else -> {
                    realm.executeTransaction {
                        val englishWord =
                            realm.where(EnglishWord::class.java)
                                .equalTo("id", englishWordId)
                                .findFirst()
                        englishWord?.english = enEditText.text.toString()
                        englishWord?.japanese = jpEditText.text.toString()
                        englishWord?.memo = memoEditText.text.toString()
                    }
                }
            }
            val intentToMoveMainPage = Intent(
                this, MainActivity::class.java)
            startActivity(intentToMoveMainPage)
        }

        // 削除処理
        deleteButton.setOnClickListener {
            try {
                realm.executeTransaction {
                    val englishWord =
                        realm.where(EnglishWord::class.java)
                            .equalTo("id", this.englishWordId)
                            .findFirst()
                    englishWord?.deleteFromRealm()

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

EnglishWordAdapter.kt

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

class EnglishWordAdapter(
    data: RealmResults<EnglishWord>?) :
    RealmBaseAdapter<EnglishWord>(data) {

    inner class ViewHolder(cell: View) {
        val english = cell.findViewById<TextView>(android.R.id.text1)
        val japanese= 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 englishWord = get(position)
            viewHolder.english.text = englishWord.english
            viewHolder.japanese.text = englishWord.japanese
        }
        return view
    }
}

参考文献

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

関連記事

ボタンイベントを処理するには
https://dk521123.hatenablog.com/entry/2013/09/25/002349
画面遷移をするには
https://dk521123.hatenablog.com/entry/2013/09/26/010117
別画面への値の受け渡し
https://dk521123.hatenablog.com/entry/2013/09/27/230328
ダイアログ表示 ~ トースト / Toast ~
https://dk521123.hatenablog.com/entry/2013/10/14/002656
DB を使用する ~ Realm 編 ~
https://dk521123.hatenablog.com/entry/2018/09/01/213649
画面コンポーネント / ListView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2013/10/10/005010
画面コンポーネント / ListView ~ Realm データを表示 ~
https://dk521123.hatenablog.com/entry/2018/09/02/130212
画面コンポーネント / FloatingActionButton
https://dk521123.hatenablog.com/entry/2020/07/03/000000

今後、参考になりそうな関連記事

Android / Kotlin で画像検索を実装する
https://dk521123.hatenablog.com/entry/2020/09/21/224542
Android / Kotlin でYoutube/Browserを起動する
https://dk521123.hatenablog.com/entry/2020/10/17/000000
設定ファイルの保存 ~ SharedPreferences ~
https://dk521123.hatenablog.com/entry/2013/10/02/005640
画面コンポーネント / ViewPager
https://dk521123.hatenablog.com/entry/2019/09/28/020307
画面コンポーネント / ViewPager2
https://dk521123.hatenablog.com/entry/2019/09/30/020307
画面コンポーネント / TabLayout
https://dk521123.hatenablog.com/entry/2020/08/18/000000
テキスト読み上げを行うには ~ TTS / Text To Speech ~
https://dk521123.hatenablog.com/entry/2018/05/13/155518
メニューを使用するには
https://dk521123.hatenablog.com/entry/2013/10/04/234613
Android StudioGitHubを連携する
https://dk521123.hatenablog.com/entry/2019/09/26/000000
画面の向きあれこれ
https://dk521123.hatenablog.com/entry/2015/09/23/125315
アラーム機能を実装するには
https://dk521123.hatenablog.com/entry/2020/07/14/000000
ダイアログを表示するには ~入門編 / アラートダイアログ ~
https://dk521123.hatenablog.com/entry/2013/10/05/214058
 画像を表示するには
https://dk521123.hatenablog.com/entry/2013/09/28/013815
音声を再生するには
https://dk521123.hatenablog.com/entry/2020/07/18/000000
動画を再生するには
https://dk521123.hatenablog.com/entry/2013/09/30/002134
画面コンポーネント / RecyclerView ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/07/21/000000
Realm に関するトラブル
https://dk521123.hatenablog.com/entry/2020/07/24/000000