build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
viewBinding {
enabled = true
}
dependencies {
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// To use Kotlin annotation processing tool (kapt)
kapt "androidx.room:room-compiler:$room_version"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.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'
}
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: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.TodoList"
tools:targetApi="31">
<activity
android:name=".AddTodoActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
</activity>
</application>
</manifest>
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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/btn_add"
app:layout_constraintTop_toTopOf="parent"
/>
<Button
android:id="@+id/btn_add"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="할 일 추가하기"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="20dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
strings.xml
<resources>
<string name="app_name">TodoList</string>
<string name="add_todo">할 일 추가하기</string>
<string name="title">제목</string>
<string name="hint_title">해야 할 일을 입력해주세요.</string>
<string name="importance">중요도</string>
<string name="importance_low">낮음</string>
<string name="importance_middle">중간</string>
<string name="importance_high">높음</string>
<string name="completion">완료</string>
</resources>
activity_add_todo.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=".AddTodoActivity">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:text="@string/title"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="@+id/edit_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="20dp"
android:hint="@string/hint_title"
android:imeOptions="actionDone"
android:inputType="text"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title" />
<TextView
android:id="@+id/importane"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:text="@string/importance"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit_title" />
<RadioGroup
android:id="@+id/radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="@id/importane">
<RadioGroup
android:id="@+id/btn_low"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importance_low" />
<RadioGroup
android:id="@+id/btn_middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importance_middle" />
<RadioGroup
android:id="@+id/btn_high"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/importance_high" />
</RadioGroup>
<Button
android:id="@+id/btn_completion"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="20dp"
android:text="@string/completion"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
db/ToDoEntity.kt
package com.example.todolist.db
import androidx.room.ColumnInfo
import androidx.room.PrimaryKey
import androidx.room.Entity
@Entity
data class ToDoEntity (
@PrimaryKey(autoGenerate = true) var id : Int? = null,
@ColumnInfo(name = "title") val title : String,
@ColumnInfo(name="importance") val importance : Int
)
db/ToDoDao.kt
package com.example.todolist.db
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
@Dao
interface ToDoDao {
@Query("SELECT * FROM ToDoEntity")
fun getAll() : List<ToDoEntity>
@Insert
fun insertTodo(todo : ToDoEntity)
@Delete
fun deleteTodo(todo : ToDoEntity)
}
AppDatabase.kt
package com.example.todolist.db
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = arrayOf(ToDoEntity::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getTodoDao() : ToDoDao
companion object {
val databaseName = "db_todo"
var appDatabase : AppDatabase? = null
fun getInstance(context : Context) : AppDatabase? {
if(appDatabase == null) {
appDatabase = Room.databaseBuilder(context,
AppDatabase::class.java,
databaseName).build()
}
return appDatabase
}
}
}
MainActivity.kt
package com.example.todolist
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.todolist.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnAdd.setOnClickListener{
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent)
}
}
}
AddTodoActivity.kt
package com.example.todolist
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.room.Dao
import com.example.todolist.databinding.ActivityAddTodoBinding
import com.example.todolist.db.AppDatabase
import com.example.todolist.db.ToDoDao
import com.example.todolist.db.ToDoEntity
class AddTodoActivity : AppCompatActivity() {
lateinit var binding : ActivityAddTodoBinding
lateinit var db : AppDatabase
lateinit var todoDao: ToDoDao
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddTodoBinding.inflate(layoutInflater)
setContentView(binding.root)
db = AppDatabase.getInstance(this)!!
todoDao = db.getTodoDao()
binding.btnCompletion.setOnClickListener {
insertTodo()
}
}
private fun insertTodo() {
val todoTitle = binding.edtTitle.text.toString()
var todoImportance = binding.radioGroup.checkedRadioButtonId
when(todoImportance) {
R.id.btn_high -> {
todoImportance = 1
}
R.id.btn_middle -> {
todoImportance = 2
}
R.id.btn_low -> {
todoImportance = 3
}
else -> {
todoImportance = -1
}
}
if(todoImportance == -1 || todoTitle.isBlank()) {
Toast.makeText(this, "모든 항목을 채워주세요.",
Toast.LENGTH_SHORT).show()
} else {
Thread {
todoDao.insertTodo(ToDoEntity(null, todoTitle, todoImportance))
runOnUiThread {
Toast.makeText(this, "추가되었습니다.",
Toast.LENGTH_SHORT).show()
finish()
}
}.start()
}
}
}
View > Tool Windows > App Inspection
MainActivity.kt
package com.example.todolist
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.todolist.databinding.ActivityMainBinding
import com.example.todolist.db.AppDatabase
import com.example.todolist.db.ToDoDao
import com.example.todolist.db.ToDoEntity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var db : AppDatabase
private lateinit var todoDao : ToDoDao
private lateinit var todoList : ArrayList<ToDoEntity>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnAdd.setOnClickListener{
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent)
}
db = AppDatabase.getInstance(this)!!
todoDao = db.getTodoDao()
getAllTodoList()
}
private fun getAllTodoList() {
Thread {
todoList = ArrayList(todoDao.getAll())
setRecyclerView()
}.start()
}
private fun setRecyclerView() {
}
}
item_todo.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tv_importance"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@color/yellow"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginStart="20dp"
android:text="2"
android:gravity="center"
android:textSize="19sp"
android:textColor="@color/white"
/>
<TextView
android:id="@+id/tv_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="GO TO GYM"
app:layout_constraintStart_toEndOf="@id/tv_importance"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="20dp"
android:layout_marginStart="10dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="red">#FF3030</color>
<color name="yellow">#FFC600</color>
<color name="green">#10A806</color>
</resources>
TodoRecyclerViewAdapter.kt
package com.example.todolist
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.todolist.databinding.ItemTodoBinding
import com.example.todolist.db.ToDoEntity
class TodoRecycleViewAdapter(private val todoList : ArrayList<ToDoEntity>) : RecyclerView.Adapter<TodoRecycleViewAdapter.MyViewHolder>() {
inner class MyViewHolder(binding : ItemTodoBinding) : RecyclerView.ViewHolder(binding.root) {
val tv_importance = binding.tvImportance
val tv_title = binding.tvTitle
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding: ItemTodoBinding =
ItemTodoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val todoData = todoList[position]
when(todoData.importance) {
1 -> {
holder.tv_importance.setBackgroundResource(R.color.red)
}
2 -> {
holder.tv_importance.setBackgroundResource(R.color.yellow)
}
3 -> {
holder.tv_importance.setBackgroundResource(R.color.green)
}
}
holder.tv_importance.text = todoData.importance.toString()
holder.tv_title.text = todoData.title
}
override fun getItemCount(): Int {
return todoList.size
}
}
MainActivity.kt
package com.example.todolist
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todolist.databinding.ActivityMainBinding
import com.example.todolist.db.AppDatabase
import com.example.todolist.db.ToDoDao
import com.example.todolist.db.ToDoEntity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var db : AppDatabase
private lateinit var todoDao : ToDoDao
private lateinit var todoList : ArrayList<ToDoEntity>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnAdd.setOnClickListener{
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent)
}
db = AppDatabase.getInstance(this)!!
todoDao = db.getTodoDao()
getAllTodoList()
}
private fun getAllTodoList() {
Thread {
todoList = ArrayList(todoDao.getAll())
setRecyclerView()
}.start()
}
private fun setRecyclerView() {
runOnUiThread {
adapter = TodoRecyclerViewAdapter(todoList)
binding.recyclerView.adapter = adapter
binding.recycleView.layoutManager = LinearLayoutManager(this)
}
}
override fun onRestart() {
super.onRestart()
getAllTodoList()
}
}
MainActivity.kt
package com.example.todolist
import android.content.DialogInterface
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todolist.databinding.ActivityMainBinding
import com.example.todolist.db.AppDatabase
import com.example.todolist.db.ToDoDao
import com.example.todolist.db.ToDoEntity
class MainActivity : AppCompatActivity() , OnItemLongClickListener{
private lateinit var binding: ActivityMainBinding
private lateinit var db : AppDatabase
private lateinit var todoDao : ToDoDao
private lateinit var todoList : ArrayList<ToDoEntity>
private lateinit var adapter: TodoRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnAdd.setOnClickListener{
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent)
}
// db 인스턴스를 가져오고 DB 작업을 할 수 있는 DAO를 가져옴
db = AppDatabase.getInstance(this)!!
todoDao = db.getTodoDao()
getAllTodoList() // 할 일 리스트 가져오기
}
private fun getAllTodoList() {
Thread{
todoList = ArrayList(todoDao.getAll())
setRecyclerView()
}.start()
}
private fun setRecyclerView() {
// 리사이클러뷰 설정
runOnUiThread{
adapter = TodoRecyclerViewAdapter(todoList, this) // 어댑터 객체 할당
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
}
}
override fun onRestart() {
super.onRestart()
getAllTodoList()
}
override fun onLongClick(position: Int) {
val builder : AlertDialog.Builder = AlertDialog.Builder(this)
builder.setTitle("할 일 삭제")
builder.setMessage("정말 삭제하시겠습니까?")
builder.setNegativeButton("취소", null)
builder.setPositiveButton("네",
object : DialogInterface.OnClickListener{
override fun onClick(p0 : DialogInterface?, p1: Int) {
deleteTodo(position)
}
})
builder.setNeutralButton("수정",
object : DialogInterface.OnClickListener{
override fun onClick(p0 : DialogInterface?, p1: Int) {
updateTodo(position)
}
})
builder.show()
}
private fun updateTodo(position: Int){
}
private fun deleteTodo(position: Int){
Thread {
todoDao.deleteTodo(todoList[position]) // DB에서 삭제
todoList.removeAt(position) // 리스트에서 삭제
runOnUiThread{
adapter.notifyDataSetChanged()
Toast.makeText(this, "삭제되었습니다.", Toast.LENGTH_SHORT).show()
}
}.start()
}
}
OnItemLongClickListener.kt
package com.example.todolist
interface OnItemLongClickListener {
fun onLongClick(position : Int)
}
TodoRecyclerViewAdapter.kt
package com.example.todolist
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.todolist.databinding.ItemTodoBinding
import com.example.todolist.db.ToDoEntity
class TodoRecycleViewAdapter(private val todoList : ArrayList<ToDoEntity>, private val listener : OnItemLongClickListener) : RecyclerView.Adapter<TodoRecycleViewAdapter.MyViewHolder>() {
inner class MyViewHolder(binding : ItemTodoBinding) : RecyclerView.ViewHolder(binding.root) {
val tv_importance = binding.tvImportance
val tv_title = binding.tvTitle
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding: ItemTodoBinding =
ItemTodoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val todoData = todoList[position]
when(todoData.importance) {
1 -> {
holder.tv_importance.setBackgroundResource(R.color.red)
}
2 -> {
holder.tv_importance.setBackgroundResource(R.color.yellow)
}
3 -> {
holder.tv_importance.setBackgroundResource(R.color.green)
}
}
holder.tv_importance.text = todoData.importance.toString()
holder.tv_title.text = todoData.title
holder.root.setOnLongClickListener {
listener.onLongClick(position)
false
}
}
override fun getItemCount(): Int {
return todoList.size
}
}
MainActivity.kt
package com.example.todolist
import android.content.DialogInterface
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todolist.databinding.ActivityMainBinding
import com.example.todolist.db.AppDatabase
import com.example.todolist.db.ToDoDao
import com.example.todolist.db.ToDoEntity
class MainActivity : AppCompatActivity(), OnItemLongClickListener {
private lateinit var binding: ActivityMainBinding
private lateinit var db : AppDatabase
private lateinit var todoDao : ToDoDao
private lateinit var todoList : ArrayList<ToDoEntity>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnAdd.setOnClickListener{
val intent = Intent(this, AddTodoActivity::class.java)
startActivity(intent)
}
db = AppDatabase.getInstance(this)!!
todoDao = db.getTodoDao()
getAllTodoList()
}
private fun getAllTodoList() {
Thread {
todoList = ArrayList(todoDao.getAll())
setRecyclerView()
}.start()
}
private fun setRecyclerView() {
runOnUiThread {
adapter = TodoRecyclerViewAdapter(todoList, this)
binding.recyclerView.adapter = adapter
binding.recycleView.layoutManager = LinearLayoutManager(this)
}
}
override fun onRestart() {
super.onRestart()
getAllTodoList()
}
override fun onLongClick(position: Int) {
val builder: AlertDialog.Builder = AlertDialog.Builder(this)
builder.setTitle("할 일 삭제")
builder.setMessage("정말 삭제하시겠습니까?")
builder.setNegativeButton("취소", null)
builder.setPositiveButton("네",
object : DialogInterface.OnClickListener {
override fun onClick(p0: DialogInterface?, p1: Int) {
deleteTodo(position)
}
}
)
builder.show()
}
private fun deleteTodo(position: Int) {
Thread {
todoDao.deleteTodo(todoList[position])
todoList.removeAt(position)
runOnUiThread {
adapter.notifyDataSetChanged()
Toast.makeText(this, "삭제되었습니다.", Toast.LENGTH_SHORT).show()
}
}.start()
}
}