From 729ff8bea975104bcff187c2743bd6d3d4b81953 Mon Sep 17 00:00:00 2001 From: andyang Date: Tue, 25 Aug 2020 22:32:00 +0800 Subject: [PATCH 1/5] basic todo list with recycler view --- app/build.gradle | 1 + .../kotlinandroidworkshop/MainActivity.kt | 15 +++++++- .../tw/andyang/kotlinandroidworkshop/Todo.kt | 6 +++ .../kotlinandroidworkshop/TodoAdapter.kt | 37 +++++++++++++++++++ app/src/main/res/layout/activity_main.xml | 22 ++++++++--- app/src/main/res/layout/item_todo.xml | 15 ++++++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt create mode 100644 app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt create mode 100644 app/src/main/res/layout/item_todo.xml diff --git a/app/build.gradle b/app/build.gradle index 07011c3..d3827dd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,6 +29,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation "androidx.recyclerview:recyclerview:1.1.0" testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt index 9435d05..d29c5c0 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt @@ -1,11 +1,24 @@ package tw.andyang.kotlinandroidworkshop -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + + val adapter = TodoAdapter() + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) + + adapter.refresh( + listOf(Todo("hello", false), Todo("world", false)) + ) } } \ No newline at end of file diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt new file mode 100644 index 0000000..3b9aea5 --- /dev/null +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt @@ -0,0 +1,6 @@ +package tw.andyang.kotlinandroidworkshop + +data class Todo( + val memo: String, + val checked: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt new file mode 100644 index 0000000..1d27382 --- /dev/null +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt @@ -0,0 +1,37 @@ +package tw.andyang.kotlinandroidworkshop + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.widget.AppCompatCheckBox +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.item_todo.view.* + +class TodoAdapter : RecyclerView.Adapter() { + + private var todos = listOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder { + return TodoViewHolder(parent) + } + + override fun getItemCount(): Int { + return todos.size + } + + override fun onBindViewHolder(holder: TodoViewHolder, position: Int) { + val todo = todos[position] + holder.checkbox.text = todo.memo + holder.checkbox.isChecked = todo.checked + } + + fun refresh(todos: List) { + this.todos = todos + notifyDataSetChanged() + } +} + +class TodoViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_todo, parent, false) +) { + val checkbox: AppCompatCheckBox = itemView.checkbox +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4fc2444..cd4b1b8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,13 +6,23 @@ android:layout_height="match_parent" tools:context=".MainActivity"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_todo.xml b/app/src/main/res/layout/item_todo.xml new file mode 100644 index 0000000..fe33dd8 --- /dev/null +++ b/app/src/main/res/layout/item_todo.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5c70dac..b42c4f8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,4 @@ KotlinAndroidWorkshop + 備忘錄 \ No newline at end of file From 193181cc4d3a3ed225299168b42af77005a88233 Mon Sep 17 00:00:00 2001 From: andyang Date: Tue, 25 Aug 2020 22:34:11 +0800 Subject: [PATCH 2/5] grid layout manager --- .../tw/andyang/kotlinandroidworkshop/MainActivity.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt index d29c5c0..328f711 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt @@ -3,7 +3,7 @@ package tw.andyang.kotlinandroidworkshop import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.GridLayoutManager import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { @@ -14,11 +14,16 @@ class MainActivity : AppCompatActivity() { val adapter = TodoAdapter() recyclerView.adapter = adapter - recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + recyclerView.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false) recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) adapter.refresh( - listOf(Todo("hello", false), Todo("world", false)) + listOf( + Todo("hello", false), + Todo("world", false), + Todo("hello", false), + Todo("world", false) + ) ) } } \ No newline at end of file From b3f80e09b4f58a261e735fde2f91dac271cf2ac9 Mon Sep 17 00:00:00 2001 From: andyang Date: Tue, 25 Aug 2020 22:36:50 +0800 Subject: [PATCH 3/5] staggered layout manager --- .../andyang/kotlinandroidworkshop/MainActivity.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt index 328f711..bb1d86a 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt @@ -2,8 +2,8 @@ package tw.andyang.kotlinandroidworkshop import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.StaggeredGridLayoutManager import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { @@ -14,13 +14,18 @@ class MainActivity : AppCompatActivity() { val adapter = TodoAdapter() recyclerView.adapter = adapter - recyclerView.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false) - recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) + recyclerView.layoutManager = StaggeredGridLayoutManager( 3, GridLayoutManager.VERTICAL) adapter.refresh( listOf( - Todo("hello", false), + Todo("hello world! hello world! hello world! hello world! hello world!", false), Todo("world", false), + Todo("hello world! hello world! hello world! hello world! hello world!", false), + Todo("hello", false), + Todo("hello", false), + Todo("hello world! hello world! hello world! hello world! hello world!", false), + Todo("hello", false), + Todo("hello", false), Todo("hello", false), Todo("world", false) ) From 855061027c85f2643b4ab77ab562931dce2e062c Mon Sep 17 00:00:00 2001 From: andyang Date: Tue, 25 Aug 2020 22:57:39 +0800 Subject: [PATCH 4/5] add view type --- .../kotlinandroidworkshop/MainActivity.kt | 37 +++++++++++------ .../tw/andyang/kotlinandroidworkshop/Todo.kt | 16 ++++++-- .../kotlinandroidworkshop/TodoAdapter.kt | 40 +++++++++++++++---- app/src/main/res/layout/activity_main.xml | 16 +------- app/src/main/res/layout/item_title.xml | 9 +++++ 5 files changed, 78 insertions(+), 40 deletions(-) create mode 100644 app/src/main/res/layout/item_title.xml diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt index bb1d86a..38b094b 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt @@ -2,8 +2,8 @@ package tw.andyang.kotlinandroidworkshop import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.StaggeredGridLayoutManager +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { @@ -14,20 +14,31 @@ class MainActivity : AppCompatActivity() { val adapter = TodoAdapter() recyclerView.adapter = adapter - recyclerView.layoutManager = StaggeredGridLayoutManager( 3, GridLayoutManager.VERTICAL) + recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) adapter.refresh( listOf( - Todo("hello world! hello world! hello world! hello world! hello world!", false), - Todo("world", false), - Todo("hello world! hello world! hello world! hello world! hello world!", false), - Todo("hello", false), - Todo("hello", false), - Todo("hello world! hello world! hello world! hello world! hello world!", false), - Todo("hello", false), - Todo("hello", false), - Todo("hello", false), - Todo("world", false) + Todo.Title(getString(R.string.todo_list_title)), + Todo.Item( + "hello world! hello world! hello world! hello world! hello world!", + false + ), + Todo.Item("world", false), + Todo.Item( + "hello world! hello world! hello world! hello world! hello world!", + false + ), + Todo.Item("hello", false), + Todo.Item("hello", false), + Todo.Item( + "hello world! hello world! hello world! hello world! hello world!", + false + ), + Todo.Item("hello", false), + Todo.Item("hello", false), + Todo.Item("hello", false), + Todo.Item("world", false) ) ) } diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt index 3b9aea5..d3a3984 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/Todo.kt @@ -1,6 +1,14 @@ package tw.andyang.kotlinandroidworkshop -data class Todo( - val memo: String, - val checked: Boolean -) \ No newline at end of file +sealed class Todo(val viewType: Int) { + data class Title(val text: String) : Todo(TYPE_TITLE) + data class Item( + val memo: String, + val checked: Boolean + ) : Todo(TYPE_ITEM) + + companion object { + const val TYPE_TITLE = 0 + const val TYPE_ITEM = 1 + } +} diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt index 1d27382..9a81dde 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt @@ -3,25 +3,34 @@ package tw.andyang.kotlinandroidworkshop import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.widget.AppCompatCheckBox +import androidx.appcompat.widget.AppCompatTextView import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.item_todo.view.* -class TodoAdapter : RecyclerView.Adapter() { +class TodoAdapter : RecyclerView.Adapter() { private var todos = listOf() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder { - return TodoViewHolder(parent) + override fun getItemViewType(position: Int): Int { + return todos[position].viewType + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + Todo.TYPE_TITLE -> TodoTitleViewHolder(parent) + else -> TodoViewHolder(parent) + } } override fun getItemCount(): Int { return todos.size } - override fun onBindViewHolder(holder: TodoViewHolder, position: Int) { - val todo = todos[position] - holder.checkbox.text = todo.memo - holder.checkbox.isChecked = todo.checked + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val todo = todos[position]) { + is Todo.Title -> (holder as TodoTitleViewHolder).bind(todo) + is Todo.Item -> (holder as TodoViewHolder).bind(todo) + } } fun refresh(todos: List) { @@ -33,5 +42,20 @@ class TodoAdapter : RecyclerView.Adapter() { class TodoViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.item_todo, parent, false) ) { - val checkbox: AppCompatCheckBox = itemView.checkbox + + private val checkbox: AppCompatCheckBox = itemView.checkbox + + fun bind(todo: Todo.Item) { + checkbox.text = todo.memo + checkbox.isChecked = todo.checked + } + +} + +class TodoTitleViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( + LayoutInflater.from(parent.context).inflate(R.layout.item_title, parent, false) +) { + fun bind(todo: Todo.Title) { + (itemView as AppCompatTextView).text = todo.text + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cd4b1b8..c45f4f8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,28 +1,14 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_title.xml b/app/src/main/res/layout/item_title.xml new file mode 100644 index 0000000..b769358 --- /dev/null +++ b/app/src/main/res/layout/item_title.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file From 247829adad79b41d3047918a477609274c9512b9 Mon Sep 17 00:00:00 2001 From: andyang Date: Tue, 25 Aug 2020 23:11:28 +0800 Subject: [PATCH 5/5] list adapter --- .../kotlinandroidworkshop/MainActivity.kt | 38 +++++++------------ .../kotlinandroidworkshop/TodoAdapter.kt | 27 ++++++------- app/src/main/res/layout/activity_main.xml | 10 +++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt index 38b094b..42fe1e7 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/MainActivity.kt @@ -8,6 +8,8 @@ import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { + private var todos = listOf() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -17,29 +19,17 @@ class MainActivity : AppCompatActivity() { recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) - adapter.refresh( - listOf( - Todo.Title(getString(R.string.todo_list_title)), - Todo.Item( - "hello world! hello world! hello world! hello world! hello world!", - false - ), - Todo.Item("world", false), - Todo.Item( - "hello world! hello world! hello world! hello world! hello world!", - false - ), - Todo.Item("hello", false), - Todo.Item("hello", false), - Todo.Item( - "hello world! hello world! hello world! hello world! hello world!", - false - ), - Todo.Item("hello", false), - Todo.Item("hello", false), - Todo.Item("hello", false), - Todo.Item("world", false) - ) - ) + todos = todos.toMutableList().apply { + add(Todo.Title(getString(R.string.todo_list_title))) + } + + adapter.submitList(todos) + + buttonAdd.setOnClickListener { + todos = todos.toMutableList().apply { + add(Todo.Item("world", false)) + } + adapter.submitList(todos) + } } } \ No newline at end of file diff --git a/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt index 9a81dde..928f01c 100644 --- a/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt +++ b/app/src/main/java/tw/andyang/kotlinandroidworkshop/TodoAdapter.kt @@ -4,15 +4,25 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.widget.AppCompatCheckBox import androidx.appcompat.widget.AppCompatTextView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.item_todo.view.* -class TodoAdapter : RecyclerView.Adapter() { +class TodoAdapter : ListAdapter( + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean { + return oldItem.viewType == newItem.viewType + } - private var todos = listOf() + override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean { + return oldItem == newItem + } + } +) { override fun getItemViewType(position: Int): Int { - return todos[position].viewType + return getItem(position).viewType } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -22,21 +32,12 @@ class TodoAdapter : RecyclerView.Adapter() { } } - override fun getItemCount(): Int { - return todos.size - } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (val todo = todos[position]) { + when (val todo = getItem(position)) { is Todo.Title -> (holder as TodoTitleViewHolder).bind(todo) is Todo.Item -> (holder as TodoViewHolder).bind(todo) } } - - fun refresh(todos: List) { - this.todos = todos - notifyDataSetChanged() - } } class TodoViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder( diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c45f4f8..c348834 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,14 +1,24 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b42c4f8..960ef27 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,5 @@ KotlinAndroidWorkshop 備忘錄 + 新增 \ No newline at end of file