事前学習的な動画⇒【はじめてのKotlinプログラミング(39)】データベースRealmを使って簡単な「お名前登録アプリ」(情報の記録と表示)


kotlindで、データベースRealmとRecyclerviewを使って
名前と年齢を登録したり削除したりできる、名簿アプリを作ってみたいと思います。

今回のアプリは前半と後半に分けて解説します。
前半のこの動画では、追加ボタンを押して、リサイクラービューで表示、つまり新規追加するところまでをやっていきます。

そして次回の後半で、追加したViewに対して、上書きして変更したり、あるいは削除したり、という機能について解説予定です。

Realm導入の記述(javaSDK公式)※SDKはjavaSDKを使用

動画

かなり長尺なので、休み休みご覧ください。ざっくりとカテゴリーわけするとこのようになります

目次

タイトル 再生時間
全体の流れ 05:35~
[1]main.xml 09:20~
[2]Realm導入(gradleファイル) 14:25~
[3]Application class(初期化) 23:30~
[4]EditActivity 28:30~
[5]モデルクラス 39:20~
[6]Intent 42:55~
[7]Realmインスタンス生成 44:50~
[8-12]データに保存 47:25~
[13]1行だけのレイアウト 1:02:15~
[14]ViewHolder 1:06:55~
[15-18]adapter 1:09:55~
[19]onStart() 1:21:05~
[20]表画面に表示(完成) 1:24:25~

コード

▼activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:text="+"
        android:textSize="20sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

▼MainActivity.kt

package com.example.realmnamelistapp

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.LayoutManager
import io.realm.Realm
import io.realm.Sort
import io.realm.kotlin.where

class MainActivity : AppCompatActivity() {
    //1)viewの取得(中身は後で代入する)
    private lateinit var recyclerView:RecyclerView
    private lateinit var realm:Realm//7)realmの変数を用意
    //19)【RecyclerView】AdapterとLayoutManagerの変数を準備
    private lateinit var recyclerAdapter:RecyclerAdapter
    private lateinit var layoutManager:LayoutManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //1)viewの取得
        val btnAdd:Button = findViewById(R.id.btnAdd)
        realm= Realm.getDefaultInstance()//7)realmのインスタンス生成

        //6)btnAddを押したらintent
        btnAdd.setOnClickListener {
            val intent = Intent(this,EditActivity::class.java)
            startActivity(intent)
        }
    }
    //20)Activityが開始された時にその都度処理される
    override fun onStart() {
        super.onStart()
        //【表示しますよ】
        //モデルクラスの全件を見つけて、ソート(並べ替え)をかける
        val realmResults = realm.where(MyModel::class.java)
            .findAll().sort("id",Sort.DESCENDING)//上の数字が大くてだんだん小さくなる(上に追加する)

        //アダプターに結果を入れますよ
        recyclerView = findViewById(R.id.rv)//ここでまずは中身recyclerViewにを入れる
        recyclerAdapter = RecyclerAdapter(realmResults)
        recyclerView.adapter = recyclerAdapter

        //縦並びに配置しますよ
        layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
    }

    //7)Realmを閉じる
    override fun onDestroy() {
        super.onDestroy()
        realm.close()
    }
}

▼build.gradle(project)

公式サイト

// Top-level build file where you can add configuration options common to all sub-projects/modules.
//https://www.mongodb.com/docs/realm/sdk/java/install/

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:10.11.1"
    }
}

plugins {
    id 'com.android.application' version '7.3.1' apply false
    id 'com.android.library' version '7.3.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}

▼build.gradle(app)

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'org.jetbrains.kotlin.kapt'
}

apply plugin: "realm-android"

android {
    namespace 'com.example.realmnamelistapp'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.realmnamelistapp"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.9.0'
    implementation 'androidx.appcompat:appcompat:1.6.0'
    implementation 'com.google.android.material:material:1.7.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

▼CustomApplication.kt

package com.example.realmnamelistapp

import android.app.Application
import io.realm.Realm
import io.realm.RealmConfiguration

//3)初期化&構築
class CustomApplication :Application(){
    override fun onCreate() {
        super.onCreate()
        Realm.init(this)

        //Configuration 構築
        val config = RealmConfiguration.Builder()
            .allowWritesOnUiThread(true)
            .allowQueriesOnUiThread(true)
            .build()
        Realm.setDefaultConfiguration(config)
    }
}

▼AndroidManifest.xml

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

    <application
        android:name=".CustomApplication"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.RealmNameListApp"
        tools:targetApi="31">
        <activity
            android:name=".EditActivity"
            android:exported="false">
            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

▼activity_edit.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".EditActivity">

    <TextView
        android:id="@+id/tvName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="Name"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/etName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:hint="text"
        android:inputType="textPersonName"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvName" />

    <TextView
        android:id="@+id/tvAge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="32dp"
        android:text="Age"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etName" />

    <EditText
        android:id="@+id/etAge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:hint="number"
        android:inputType="number"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tvAge" />

    <Button
        android:id="@+id/btnSave"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="32dp"
        android:text="save"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etAge" />

    <Button
        android:id="@+id/btnDel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginEnd="16dp"
        android:text="del"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etAge" />
</androidx.constraintlayout.widget.ConstraintLayout>

▼EditActivity.kt

package com.example.realmnamelistapp

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where

class EditActivity : AppCompatActivity() {
    //8)Realmの変数宣言
    private lateinit var realm :Realm

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_edit)
        //4)Viewの取得
        val etName:EditText = findViewById(R.id.etName)
        val etAge:EditText = findViewById(R.id.etAge)
        val btnSave:Button = findViewById(R.id.btnSave)
        val btnDel:Button = findViewById(R.id.btnDel)

        //8)ここでもRealmインスタンスを生成
        realm = Realm.getDefaultInstance()

        //9)Saveボタンを押したら~
        btnSave.setOnClickListener {
            //上書き用の変数を用意
            var name:String =""
            var age :Long = 0
            //10)入力された文字が空文字でなければ~(変数に代入)
            if(!etName.text.isNullOrEmpty()){
                name =etName.text.toString()
            }
            if(!etAge.text.isNullOrEmpty()){
                age=etAge.text.toString().toLong()
            }

            //11)【DBに書き込みますよ】 / Transaction{}
            realm.executeTransaction {
                val currentId = realm.where<MyModel>().max("id")//現時点のid(の最高値)を取得
                val nextId =(currentId?.toLong() ?:0L)+1L //最高値に1を追加(最高値が0なら1に)←行を追加するイメージ
                //モデルクラス(nextId番目)に値をセット
                val myModel =realm.createObject<MyModel>(nextId)
                myModel.name = name
                myModel.age = age
            }
            //12)toastでメッセージを表示
            Toast.makeText(applicationContext,"保存しました",Toast.LENGTH_SHORT).show()
            finish()
        }
    }

    //8)Realm閉じる
    override fun onDestroy() {
        super.onDestroy()
        realm.close()
    }
}

▼MyModel.kt

package com.example.realmnamelistapp

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey

open class MyModel :RealmObject(){
    @PrimaryKey
    var id:Long =0
    var name:String =""
    var age:Long =0
}

▼one_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/oneTvName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="名前が表示される"
        android:textSize="24sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/oneTvAge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="年齢が表示される"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/oneTvName" />

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="?android:attr/listDivider"
        app:layout_constraintBottom_toBottomOf="@+id/oneTvAge" />
</androidx.constraintlayout.widget.ConstraintLayout>

▼ViewHolderItem.kt

package com.example.realmnamelistapp

import android.view.View
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class ViewHolderItem(v:View):RecyclerView.ViewHolder(v) {
    //View(xml)の方から、指定のidを見つけてくる(v経由で)
    var oneTvName:TextView =v.findViewById(R.id.oneTvName)
    var oneTvAge:TextView =v.findViewById(R.id.oneTvAge)
}

▼RecyclerAdapter.kt

package com.example.realmnamelistapp

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.realm.RealmResults

//15)Adapter
//17-1)結果を受け取る引数(=受け皿)RealmResults ← 登録データ全件(Realmクエリの実行結果)を取得する
class RecyclerAdapter(realmResults:RealmResults<MyModel>):RecyclerView.Adapter<ViewHolderItem>() {
    //17-2)RecyclerAdapter()の引数realmResultsを展開していく変数を用意
    private val rResults:RealmResults<MyModel> =realmResults

    //16)1行だけのレイアウト表示
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderItem {
        val  oneXml = LayoutInflater.from(parent.context)
            .inflate(R.layout.one_layout,parent,false)
        return ViewHolderItem(oneXml)
    }

    //17-3)position番目のデータを表示
    override fun onBindViewHolder(holder: ViewHolderItem, position: Int) {
        val myModel = rResults[position]//position番目の結果を取得
        holder.oneTvName.text = myModel?.name.toString() //position番目のnameを代入
        holder.oneTvAge.text = myModel?.age.toString()//position番目のageを代入
    }

    //18)リスト(=結果件数)の件数(=サイズ)
    override fun getItemCount(): Int {
        return rResults.size
    }
}

参考サイト

Install Realm – Java SDK

Android Tutorial on realm database | local database | android coding

【Androidアプリチュートリアル】 メモアプリを作ろう

スマホアプリ開発つまずきポイント:Realmデータベースの使い方~金宏和實「作ればわかる!Androidプログラミング」

Realmの使い方 初心者向け Kotlin

    コメントを残す