公開日:2020/04/19更新日:2020/08/03
【Kotlin MVP】MVPアーキテクチャで、TODOアプリを作成する #2

アプリ開発におけるアーキテクチャ(設計)の1つに、MVPというものがあります。
本記事は、ToDoアプリの作成を通して、MVPについて学習するシリーズの第2弾です。
第2弾の今回は、タスクをリスト表示するListViewを実装します。
なお、本シリーズで実装するTODOアプリのソースコードは、todo-sample | GitHubにて公開しております。
目次
ListViewの実装
データクラスの実装
まずは、一覧表示するタスクのデータクラスを作成します。
- Task.kt
data class Task(
val id: Int,
val state: Int,
val description: String
)
Taskは、id(インデックス)、state(状態)、description(タスク内容)をパラメータとして持つようにしました。
stateはInt型で、0がTODO、1がDOING、2がDONEとして実装していきます。
Adapterの実装
続いて、ListViewのAdapterを実装します。
- MainFragment.kt
- task_item.xml
class MainFragment : Fragment() {
(省略)
private class TasksAdapter(private val tasks: List<Task>): BaseAdapter() {
private val stateTexts = listOf(R.string.todo, R.string.doing, R.string.done)
private val stateColors = listOf(R.color.todo, R.color.doing, R.color.done)
override fun getCount() = tasks.size
override fun getItem(i: Int) = tasks[i]
override fun getItemId(i: Int) = i.toLong()
override fun getView(i: Int, view: View?, viewGroup: ViewGroup): View {
val task = getItem(i)
val rowView = view ?: LayoutInflater.from(viewGroup.context).inflate(R.layout.task_item, viewGroup, false)
rowView.findViewById<TextView>(R.id.taskState).apply {
text = context.getString(stateTexts[task.state])
setTextColor(ContextCompat.getColor(context, stateColors[task.state]))
}
rowView.findViewById<TextView>(R.id.taskDescription).apply {
text = task.description
}
return rowView
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/taskState"
android:textSize="18sp"
android:layout_width="80dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/taskDescription"
android:textSize="18sp"
android:textColor="@color/black"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ImageView
android:id="@+id/taskDeleteButton"
android:src="@drawable/ic_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
TasksAdapterというAdapterクラスを作成しました。別ファイルとして作成してもいいのですが、今回は、MainFragment内に実装いたしました。
各タスクのViewは、
TODOやDONEなどの状態を表示するためのTextView、
タスクの内容を表示するためのTextView、
タスクを削除するためのボタンとなるImageView
で構成されております。
ListViewの実装
最後に、MainFragmentにListViewを実装し、そのListViewのAdapterに、TasksAdapterを設定します。
- MainFragment.kt
- task_item.xml
class MainFragment : Fragment() {
(省略)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addTaskButton.setOnClickListener {
Toast.makeText(activity, "Add Tapped", Toast.LENGTH_SHORT).show()
}
// TODO ダミーデータ
val tasks = arrayListOf<Task>()
val task1 = Task(0, 0, "Task1")
val task2 = Task(1, 0, "Task2")
val task3 = Task(2, 1, "Task3")
val task4 = Task(3, 2, "Task4")
val task5 = Task(4, 2, "Task5")
tasks.add(task1)
tasks.add(task2)
tasks.add(task3)
tasks.add(task4)
tasks.add(task5)
listView.adapter = TasksAdapter(tasks)
}
(省略)
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addTaskButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_add"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_margin="24dp" />
</RelativeLayout>
ダミーデータとして、Taskのリストを作成し、TasksAdapterの生成時に引数として渡しております。
将来的には、データベースから取得したTaskのリストを渡すように修正していきます。
実行結果
この章の修正内容はListViewの実装 | GitHubからも確認頂けます。
イベントの実装
Interfaceの実装
続いて、リストの状態表示、内容、削除ボタンがタップされた際のイベントをそれぞれ実装していきます。まずは、Interfaceの実装からです。
- MainFragment.kt
class MainFragment : Fragment() {
(省略)
interface TaskItemListener {
fun onStateClick(task: Task)
fun onDescriptionClick(task: Task)
fun onDescriptionClick(task: Task)
}
}
TaskItemListenerというInterfaceを実装しました。
TaskItemListenerには、onStateClick()、onDescriptionClick()、onDescriptionClick()というCallbackが実装されております。
Callbackの呼び出し処理の実装
Interfaceの実装ができましたら、TaskAdapterにて、Interfaceに実装されているCallbackを呼び出す処理を実装します。
- MainFragment.kt
class MainFragment : Fragment() {
(省略)
private class TasksAdapter(private val tasks: List<Task>, private val listener: TaskItemListener): BaseAdapter() {
(省略)
override fun getView(i: Int, view: View?, viewGroup: ViewGroup): View {
val task = getItem(i)
val rowView = view ?: LayoutInflater.from(viewGroup.context).inflate(R.layout.task_item, viewGroup, false)
rowView.findViewById<TextView>(R.id.taskState).apply {
text = context.getString(stateTexts[task.state])
setTextColor(ContextCompat.getColor(context, stateColors[task.state]))
setOnClickListener {
listener.onStateClick(task)
}
}
rowView.findViewById<TextView>(R.id.taskDescription).apply {
text = task.description
setOnClickListener {
listener.onDescriptionClick(task)
}
}
rowView.findViewById<ImageView>(R.id.taskDeleteButton).setOnClickListener {
listener.onDeleteClick(task)
}
return rowView
}
}
(省略)
}
状態、内容、削除ボタンがタップされた際に、Callbackが呼ばれるように実装しました。
また、コンストラクタで、TaskItemListenerを受け取るように実装を加えております。
CallbackのOverride
最後に、MainFragmentにて、TaskItemListenerをimplementしたobjectを作成し、Callbackをoverrideして、イベント発生時に行いたい処理を実装します。
- MainFragment.kt
class MainFragment : Fragment() {
(省略)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(省略)
listView.adapter = TasksAdapter(tasks, itemListener)
}
val itemListener = object : TaskItemListener {
override fun onStateClick(task: Task) {
Toast.makeText(activity, "onStateClick : " + task.description, Toast.LENGTH_SHORT).show()
}
override fun onDescriptionClick(task: Task) {
Toast.makeText(activity, "onDescriptionClick : " + task.description, Toast.LENGTH_SHORT).show()
}
override fun onDeleteClick(task: Task) {
Toast.makeText(activity, "onDeleteClick : " + task.description, Toast.LENGTH_SHORT).show()
}
}
}
イベント発生時には、Callbackのメソッド名と、タスクの内容が、トーストで表示されるように実装しました。将来的には、タスクの状態変更処理、タスクの内容変更処理、タスクの削除処理を実装する予定です。
このobject(itemListener)は、TasksAdapterの生成時に引数として受け渡し、2-2. Callbackの呼び出し処理の実装 で実装した処理に繋がっていきます。
実行結果
この章の修正内容はイベントの実装 | GitHubからも確認頂けます。
今回の実装はここまでとなります。
次回は、Roomライブラリを使用したデータベースの実装していく予定です。
ありがとうございました。