おはようございます。@naokiurです。
以前から興味があった、KotlinでAndroid開発を、
こっそりやっています。
よくある、 SQLiteOpenHelper
を用いたデータベースアクセスを実装することができたので、
その内容を書き留めておきたいと思います。
環境
- MacBook Pro (Retina 13-inch、Early 2015)
- macOS High Sierra Capitan 10.13.2
- Kotlin 1.2
- Android Studio 3.1.2
目標
- 画面に入力したタスク情報を、データベースに登録することができる。
- よくあるタスク管理アプリ
実施したこと
- 管理したいタスクの情報を決める
- Contractクラスを作成する
- Modelクラスを作成する
- DatabaseHelperクラスを作成する
- 画面に入力された情報を、DatabaseHelperクラスを用いて登録する、Activityクラスを作成する
管理したいタスクの情報を決める
シンプルに、1タスクは、以下のような情報を管理することとしました。
- タスク名
- ステータス
- Doing(コード値: 0)
- Done(コード値: 1)
- 開始日
- 終了日
- 更新時刻
Contractクラスを作成する
データベーススキーマなどを定義する、Contractクラスを作成します。
今回は、1テーブル分なので効果は出ませんが、
データベース全体のテーブルの情報を、テーブル単位のインナークラスとして持っておくのが良い方法、
と書いてあるように見受けられました。(違ったらごめんなさい)
A good way to organize a contract class is to put definitions that are global to your whole database in the root level of the class.
Then create an inner class for each table. Each inner class enumerates the corresponding table's columns.
管理したいタスクの情報を元に、
以下のようなContractクラスを作成しました。
package jp.ne.naokiur.todomanagement.strage
import android.provider.BaseColumns
object DBContract {
class TaskEntry : BaseColumns {
companion object {
const val TABLE_NAME = "tasks"
const val TASK_NAME = "task_name"
const val STATUS = "status"
const val BEGIN_DATE = "begin_date"
const val END_DATE = "end_date"
const val UPDATE_TIME = "update_time"
}
}
}
Modelクラスを作成する
タスクデータを持ち運ぶためのModelクラスを作成します。
いわゆるDTOという理解をしています。
Contractクラスの定義を元に、
以下のようなModelクラスを作成しました。
package jp.ne.naokiur.todomanagement.models
class TaskModel(val name: String, val status: String, val beginData: Long, val endDate: Long, val updateTime: Long)
DatabaseHelperクラスを作成する
データベースへアクセスするDatabaseHelperクラスを作成します。
ライブラリなどはあるのですが、今回はオーソドックスに、
SQLiteOpenHelperクラスを継承して作成します。
データベースの作成、およびINSERTができるように、
以下のようなDatabaseHelperクラスを作成しました。
package jp.ne.naokiur.todomanagement.strage
import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import android.database.sqlite.SQLiteOpenHelper
import jp.ne.naokiur.todomanagement.models.TaskModel
class TasksDatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
@Throws(SQLiteException::class)
fun insertTask(task: TaskModel): Boolean {
val db = writableDatabase
val values = ContentValues()
values.put(DBContract.TaskEntry.TASK_NAME, task.name)
values.put(DBContract.TaskEntry.STATUS, task.status)
values.put(DBContract.TaskEntry.BEGIN_DATE, task.beginData)
values.put(DBContract.TaskEntry.END_DATE, task.endDate)
values.put(DBContract.TaskEntry.UPDATE_TIME, task.updateTime)
val rowId = db.insert(DBContract.TaskEntry.TABLE_NAME, null, values)
return true
}
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(SQL_CREATE_ENTRIES)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.execSQL(SQL_DELETE_ENTRIES)
db?.execSQL(SQL_CREATE_ENTRIES)
}
override fun onDowngrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
onUpgrade(db, oldVersion, newVersion)
}
companion object {
const val DATABASE_VERSION = 1
const val DATABASE_NAME = "TASKS.db"
private const val SQL_CREATE_ENTRIES =
"CREATE TABLE " + DBContract.TaskEntry.TABLE_NAME + " (" +
DBContract.TaskEntry.TASK_NAME + " TEXT ," +
DBContract.TaskEntry.STATUS + " TEXT," +
DBContract.TaskEntry.BEGIN_DATE + " INTEGER," +
DBContract.TaskEntry.END_DATE + " INTEGER," +
DBContract.TaskEntry.UPDATE_TIME + " INTEGER)"
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + DBContract.TaskEntry.TABLE_NAME
}
}
画面に入力された情報を、DatabaseHelperクラスを用いて登録する、Activityクラスを作成する
シンプルに、ボタンが押下された場合、
入力された値をデータベースに登録するActivityを作成します。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val taskDatabaseHelper = TasksDatabaseHelper(this)
val cal = Calendar.getInstance()
val format = "yyyy/MM/dd"
val dateFormat = SimpleDateFormat(format, Locale.JAPANESE)
val beginDateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, month)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
begin_date!!.text = dateFormat.format(cal.time)
}
val limitDateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, month)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
limit_date!!.text = dateFormat.format(cal.time)
}
val beginDate = dateFormat.parse(begin_date.text.toString()).time
val limitDate = dateFormat.parse(limit_date.text.toString()).time
save_button.setOnClickListener { view ->
val task = TaskModel(
task_name.text.toString(),
"0", // 初期値を `Doing`とする
beginDate,
limitDate,
Calendar.getInstance().timeInMillis
)
val result = taskDatabaseHelper.insertTask(task)
if (result) {
Toast.makeText(this, "new Task Added!", LENGTH_LONG).show()
}
}
結果
画面に入力した値が、データベースに登録されていることを確認することができました。
(エミュレータ、DB Browser for SQliteで確認しました。)
今後
- デザインががたがたなので調整する…
- 画面上で現在のタスクを確認できるようにする
- Doing/Doneはチェックボックスでやるつもりなので、Booleanに変換できるようにする