Android
 Computer >> コンピューター >  >> プログラミング >> Android

KotlinでAndroidアプリにSQLiteデータベースとRecyclerViewを連携させる方法を解説

この記事では、Kotlinを使ってAndroidアプリ内のデータベース(SQLite)とRecyclerViewを連携させる方法を、連絡先管理アプリを例に段階的に解説します。データベースへの追加・編集・削除といった基本操作を含む実装例なので、CRUD処理の学習にも最適です。

ステップ1:新規プロジェクトを作成する

Android Studioを起動し、「File」→「New Project」を選択して、必要な情報を入力しながら新しいプロジェクトを作成します。

ステップ2:activity_main.xml にコードを追加する

まず、メイン画面となるレイアウトファイル「res/layout/activity_main.xml」に以下のコードを記述します。連絡先一覧を表示するRecyclerViewと、新規追加用のボタンを配置しています。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="4dp"
    tools:context=".MainActivity">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/myContactList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="50dp"
        android:paddingBottom="8dp" />
    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="4dp"
        android:padding="2dp"
        android:text="ADD" />
</FrameLayout>

ステップ3:MainActivity.kt を実装する

次に「src/MainActivity.kt」に以下のコードを追加します。このクラスでは、データベースから連絡先リストを取得してRecyclerViewに表示し、ボタン押下時に新規登録用ダイアログを表示する処理を行っています。

import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
    private lateinit var dataBase: SqliteDatabase
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "KotlinApp"
        val contactView: RecyclerView = findViewById(R.id.myContactList)
        val linearLayoutManager = LinearLayoutManager(this)
        contactView.layoutManager = linearLayoutManager
        contactView.setHasFixedSize(true)
        dataBase = SqliteDatabase(this)
        val allContacts = dataBase.listContacts()
        if (allContacts.size > 0) {
            contactView.visibility = View.VISIBLE
            val mAdapter = ContactAdapter(this, allContacts)
            contactView.adapter = mAdapter
        }
        else {
            contactView.visibility = View.GONE
            Toast.makeText(
                this,
                "There is no contact in the database. Start adding now",
                Toast.LENGTH_LONG
            ).show()
        }
        val btnAdd: Button = findViewById(R.id.btnAdd)
        btnAdd.setOnClickListener { addTaskDialog() }
    }
    private fun addTaskDialog() {
        val inflater = LayoutInflater.from(this)
        val subView = inflater.inflate(R.layout.add_contacts, null)
        val nameField: EditText = subView.findViewById(R.id.enterName)
        val noField: EditText = subView.findViewById(R.id.enterPhoneNum)
        val builder = AlertDialog.Builder(this)
        builder.setTitle("Add new CONTACT")
        builder.setView(subView)
        builder.create()
        builder.setPositiveButton("ADD CONTACT") { _, _ ->
        val name = nameField.text.toString()
        val phoneNum = noField.text.toString()
        if (TextUtils.isEmpty(name)) {
            Toast.makeText(
                this@MainActivity,
                "Something went wrong. Check your input values",
                Toast.LENGTH_LONG
            ).show()
        }
        else {
            val newContact = Contacts(name, phoneNum)
            dataBase.addContacts(newContact)
            finish()
            startActivity(intent)
        }
    }
    builder.setNegativeButton("CANCEL") { _, _ -> Toast.makeText(this@MainActivity, "Task cancelled",
Toast.LENGTH_LONG).show()}
        builder.show()
    }
    override fun onDestroy() {
        super.onDestroy()
        dataBase.close()
    }
}

ステップ4:Kotlinクラスファイルを作成する

続いて、以下の4つのKotlinクラスファイルを作成し、それぞれ対応するコードを記述していきます。

Contacts.kt

連絡先データ(ID・名前・電話番号)を保持するモデルクラスです。

class Contacts {
    var id = 0
    var name: String
    var phno: String
    internal constructor(name: String, phno: String) {
        this.name = name
        this.phno = phno
    }
    internal constructor(id: Int, name: String, phno: String) {
        this.id = id
        this.name = name
        this.phno = phno
    }
}

ContactViewHolder.kt

RecyclerViewの各行ビューへの参照を保持するViewHolderクラスです。

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
class ContactViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var tvName: TextView = itemView.findViewById(R.id.contactName)
    var tvPhoneNum: TextView = itemView.findViewById(R.id.phoneNum)
    var deleteContact: ImageView = itemView.findViewById(R.id.deleteContact)
    var editContact: ImageView = itemView.findViewById(R.id.editContact)
}

ContactAdapter.kt

RecyclerViewにデータを供給するアダプタークラスです。検索機能(Filterable)や、行ごとの編集・削除処理もここに実装されています。

import android.app.Activity
import android.content.Context
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Filter
import android.widget.Filterable
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView
import java.util.*
internal class ContactAdapter(private val context: Context, listContacts: ArrayList<Contacts>) :
RecyclerView.Adapter<ContactViewHolder>(), Filterable {
    private var listContacts: ArrayList<Contacts>
    private val mArrayList: ArrayList<Contacts>
    private val mDatabase: SqliteDatabase
    init {
        this.listContacts = listContacts
        this.mArrayList = listContacts
        mDatabase = SqliteDatabase(context)
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
        val view =
        LayoutInflater.from(parent.context).inflate(R.layout.contact_list_layout, parent, false)
        return ContactViewHolder(view)
    }
    override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
        val contacts = listContacts[position]
        holder.tvName.text = contacts.name
        holder.tvPhoneNum.text = contacts.phno
        holder.editContact.setOnClickListener { editTaskDialog(contacts) }
        holder.deleteContact.setOnClickListener {
            mDatabase.deleteContact(contacts.id)
            (context as Activity).finish()
            context.startActivity(context.intent)
        }
    }
    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(charSequence: CharSequence): FilterResults {
                val charString = charSequence.toString()
                listContacts = if (charString.isEmpty()) {
                    mArrayList
                }
                else {
                    val filteredList = ArrayList<Contacts>()
                    for (contacts in mArrayList) {
                        if (contacts.name.toLowerCase().contains(charString)) {
                            filteredList.add(contacts)
                        }
                    }
                    filteredList
                }
                val filterResults = FilterResults()
                filterResults.values = listContacts
                return filterResults
            }
            override fun publishResults(
                charSequence: CharSequence,
                filterResults: FilterResults
            )
            {
                listContacts =
                filterResults.values as ArrayList<Contacts>
                notifyDataSetChanged()
            }
        }
    }
    override fun getItemCount(): Int {
        return listContacts.size
    }
    private fun editTaskDialog(contacts: Contacts) {
        val inflater = LayoutInflater.from(context)
        val subView = inflater.inflate(R.layout.add_contacts, null)
        val nameField: EditText = subView.findViewById(R.id.enterName)
        val contactField: EditText = subView.findViewById(R.id.enterPhoneNum)
        nameField.setText(contacts.name)
        contactField.setText(contacts.phno)
        val builder = AlertDialog.Builder(context)
        builder.setTitle("Edit contact")
        builder.setView(subView)
        builder.create()
        builder.setPositiveButton(
            "EDIT CONTACT"
        ) { _, _ ->
            val name = nameField.text.toString()
            val phNo = contactField.text.toString()
            if (TextUtils.isEmpty(name)) {
                Toast.makeText(
                    context,
                    "Something went wrong. Check your input values",
                    Toast.LENGTH_LONG
                ).show()
            }
            else {
                mDatabase.updateContacts(
                    Contacts(
                        Objects.requireNonNull<Any>(contacts.id) as Int,
                        name,
                        phNo
                    )
                )
                (context as Activity).finish()
                context.startActivity(context.intent)
            }
        }
        builder.setNegativeButton(
            "CANCEL"
        ) { _, _ -> Toast.makeText(context, "Task cancelled", Toast.LENGTH_LONG).show() }
        builder.show()
    }
}

SqliteDatabase.kt

SQLiteOpenHelperを継承したデータベース操作クラスです。テーブル作成、および連絡先の一覧取得・追加・更新・削除の各メソッドを実装しています。

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import java.util.*
class SqliteDatabase internal constructor(context: Context?) :
SQLiteOpenHelper(
    context,
    DATABASE_NAME,
    null,
    DATABASE_VERSION
) {
    override fun onCreate(db: SQLiteDatabase) {
        val createContactTable = ("CREATE TABLE "
        + TABLE_CONTACTS + "(" + COLUMN_ID
        + " INTEGER PRIMARY KEY,"
        + COLUMN_NAME + " TEXT,"
        + COLUMN_NO + " INTEGER" + ")")
        db.execSQL(createContactTable)
    }
    override fun onUpgrade(
        db: SQLiteDatabase,
        oldVersion: Int,
        newVersion: Int
    ) {
        db.execSQL("DROP TABLE IF EXISTS $TABLE_CONTACTS")
        onCreate(db)
    }
    fun listContacts(): ArrayList<Contacts> {
        val sql = "select * from $TABLE_CONTACTS"
        val db = this.readableDatabase
        val storeContacts =
        ArrayList<Contacts>()
        val cursor = db.rawQuery(sql, null)
        if (cursor.moveToFirst()) {
            do {
                val id = cursor.getString(0).toInt()
                val name = cursor.getString(1)
                val phno = cursor.getString(2)
                storeContacts.add(Contacts(id, name, phno))
            }
            while (cursor.moveToNext())
        }
        cursor.close()
        return storeContacts
    }
    fun addContacts(contacts: Contacts) {
        val values = ContentValues()
        values.put(COLUMN_NAME, contacts.name)
        values.put(COLUMN_NO, contacts.phno)
        val db = this.writableDatabase
        db.insert(TABLE_CONTACTS, null, values)
    }
    fun updateContacts(contacts: Contacts) {
        val values = ContentValues()
        values.put(COLUMN_NAME, contacts.name)
        values.put(COLUMN_NO, contacts.phno)
        val db = this.writableDatabase
        db.update(
            TABLE_CONTACTS,
            values,
            "$COLUMN_ID = ?",
            arrayOf(contacts.id.toString())
        )
    }
    fun deleteContact(id: Int) {
        val db = this.writableDatabase
        db.delete(
            TABLE_CONTACTS,
            "$COLUMN_ID = ?",
            arrayOf(id.toString())
        )
    }
    companion object {
        private const val DATABASE_VERSION = 5
        private const val DATABASE_NAME = "Contacts"
        private const val TABLE_CONTACTS = "Contacts"
        private const val COLUMN_ID = "_id"
        private const val COLUMN_NAME = "contactName"
        private const val COLUMN_NO = "phoneNumber"
    }
}

ステップ5:レイアウトリソースファイルを作成する

入力ダイアログ用とリスト行表示用の2つのレイアウトファイル(add_contacts.xml と contact_list_layout.xml)を作成し、以下のコードを追加します。

add_contacts.xml

名前と電話番号を入力するためのEditTextを縦に並べたシンプルなレイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <EditText
        android:id="@+id/enterName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:hint="Enter Name"
        android:inputType="text"
        android:maxLines="1"
        android:padding="12dp"
        android:textSize="12sp" />
    <EditText
        android:id="@+id/enterPhoneNum"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginEnd="8dp"
        android:hint="Enter Number"
        android:inputType="phone"
        android:maxLines="1"
        android:padding="12dp"
        android:textSize="12sp" />
</LinearLayout>

contact_list_layout.xml

CardViewを使って、連絡先の名前・電話番号に加え、編集・削除アイコンを横並びで表示する行レイアウトです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="1dp"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="12dp">
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    <TextView
        android:id="@+id/contactName"
        android:layout_width="220dp"
        android:layout_height="wrap_content"
        android:textSize="12sp"
        android:textStyle="bold" />
    <TextView
        android:id="@+id/phoneNum"
        android:layout_width="220dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/contactName"
        android:textSize="12sp"
        android:textStyle="bold" />
    </RelativeLayout>
    <ImageView
        android:id="@+id/editContact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:contentDescription="TODO"
        android:src="@drawable/ic_baseline_edit_24" />
    <ImageView
        android:id="@+id/deleteContact"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:contentDescription="TODO"
        android:src="@drawable/ic_baseline_remove_24" />
    </LinearLayout>
</androidx.cardview.widget.CardView>

ステップ6:AndroidManifest.xml にコードを追加する

最後に、マニフェストファイル「androidManifest.xml」に以下のコードを記述します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.q11">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

アプリを実行してみよう

それでは、実際にアプリケーションを起動してみましょう。ここでは、実機のAndroidスマートフォンがパソコンに接続されているものとして説明を進めます。Android Studioからプロジェクト内のアクティビティファイルを開き、ツールバーにあるRunアイコンをクリックしてください。表示された候補から自分のモバイルデバイスを選択すると、端末上にアプリの初期画面が表示されます。

アプリを起動すると、まず空の状態でトーストメッセージが表示されます。「ADD」ボタンをタップして連絡先を追加すると、RecyclerViewにカード形式のリストが表示され、各行の編集アイコン・削除アイコンからデータベースの内容を更新できることが確認できます。

  1. 【Android】データベースとRecyclerViewを連携させる方法を実装例つきで解説

    この記事では、AndroidアプリでRecyclerViewとデータベース(SQLite)を連携させて使用する方法を、実際のサンプルコードとともに解説します。連絡先の登録・表示・編集・削除ができるシンプルなアプリを題材に、実装手順をステップごとに見ていきましょう。ステップ1:プロジェクトの作成と依存関係の追加まず、Android Studioで新しいプロジェクトを作成します。メニューから「File → New Project」を選択し、必要事項を入力してプロジェクトを作成してください。続いて、build.gradle(Module: app)に以下の依存関係を追加します。implementat

  2. AndroidアプリでSQLiteデータベースを使う方法を徹底解説【CRUD実装の手順】

    この記事では、AndroidアプリケーションでSQLiteデータベースを活用する方法を、実際のコード例とともに段階的に解説します。データの追加・一覧表示・更新・削除(CRUD操作)をすべて網羅したサンプルアプリを作成しながら、SQLiteの基本的な使い方をマスターしましょう。全体の流れ本チュートリアルでは、サッカー選手の名前と所属国を管理するシンプルなアプリを題材にします。主な構成要素は以下の通りです。DatabaseHelper:SQLiteOpenHelperを継承し、データベースの作成・管理を行うクラスDataBaseManager:データの挿入・取得・更新・削除を担当するクラスMain