diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..c4e4683
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+BCSD_Android_2025-1
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..639c779
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..c224ad5
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..02a7102
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5791375..7272f8a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
+ id("kotlin-kapt")
}
android {
@@ -17,6 +18,14 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
+ buildFeatures {
+ dataBinding = true
+ }
+
+ kapt {
+ correctErrorTypes = true
+ }
+
buildTypes {
release {
isMinifyEnabled = false
@@ -42,6 +51,11 @@ dependencies {
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
+ implementation(libs.androidx.lifecycle.viewmodel.ktx)
+ implementation(libs.kotlinx.coroutines.android)
+ implementation(libs.androidx.room.runtime)
+ implementation(libs.androidx.room.ktx)
+ kapt(libs.androidx.room.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4c80941..1819965 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,6 +12,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.BCSD_Android_20251"
tools:targetApi="31">
+
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt
new file mode 100644
index 0000000..e205f6b
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/AddEditActivity.kt
@@ -0,0 +1,52 @@
+package com.example.bcsd_android_2025_1
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import com.example.bcsd_android_2025_1.databinding.ActivityAddEditBinding
+
+
+class AddEditActivity : AppCompatActivity(){
+ private lateinit var binding: ActivityAddEditBinding
+ private val viewModel:WordViewModel by viewModels()
+
+ private var wordId: Int?= null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+
+ super.onCreate(savedInstanceState)
+ binding = ActivityAddEditBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ val editWordText = intent.getStringExtra("word_text")
+ val editMeaning = intent.getStringExtra("word_meaning")
+ wordId = intent.getIntExtra("word_id", -1).takeIf { it != -1 }
+
+ binding.wordEdittext.setText(editWordText)
+ binding.meaningEdittext.setText(editMeaning)
+
+ binding.addButton.setOnClickListener {
+ val wordText = binding.wordEdittext.text.toString()
+ val meaning = binding.meaningEdittext.text.toString()
+ if (wordText.isNotBlank() && meaning.isNotBlank()) {
+ val word = WordListData(wordId ?: 0, wordText, meaning)
+ if (wordId != null) {
+ viewModel.update(word)
+ binding.wordEdittext.setText("")
+ binding.meaningEdittext.setText("")
+ val resultIntent = Intent()
+ resultIntent.putExtra("edited_word", word.word)
+ resultIntent.putExtra("edited_meaning", word.meaning)
+ setResult(Activity.RESULT_OK, resultIntent)
+ finish()
+
+ } else {
+ viewModel.insert(word)
+ }
+ finish()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt b/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt
index 3ffa0eb..51ef211 100644
--- a/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/MainActivity.kt
@@ -1,14 +1,78 @@
package com.example.bcsd_android_2025_1
+import android.app.Activity
+import android.content.Intent
import android.os.Bundle
-import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.view.ViewCompat
-import androidx.core.view.WindowInsetsCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.example.bcsd_android_2025_1.databinding.ActivityMainBinding
+
class MainActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityMainBinding
+ private val viewModel: WordViewModel by viewModels()
+ private lateinit var editActivityLauncher: ActivityResultLauncher
+
+
+ private val adapter by lazy {
+ WordAdapter(onTopClick = { viewModel.setTopWord(it) })
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+
+ editActivityLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ val data = result.data
+ val editedWord = data?.getStringExtra("edited_word") ?: ""
+ val editedMeaning = data?.getStringExtra("edited_meaning") ?: ""
+
+ // 상단 TextView 업데이트
+ binding.wordTextview.text = editedWord
+ binding.meaningTextview.text = editedMeaning
+ }
+ }
+
+ binding.recyclerView.adapter = adapter
+ binding.recyclerView.layoutManager = LinearLayoutManager(this)
+
+ binding.editButton.setOnClickListener {
+ viewModel.topWord.value?.let {
+ val intent = Intent(this, AddEditActivity::class.java).apply {
+ putExtra("word_id", it.id)
+ putExtra("word_text", it.word)
+ putExtra("word_meaning", it.meaning)
+ }
+ editActivityLauncher.launch(intent)
+ }
+ }
+
+ binding.deleteButton.setOnClickListener {
+ viewModel.topWord.value?.let {
+ viewModel.delete(it)
+ binding.wordTextview.text = ""
+ binding.meaningTextview.text = ""
+ }
+ }
+
+ viewModel.allWords.observe(this) {
+ adapter.submitList(it)
+ }
+
+ viewModel.topWord.observe(this) {
+ binding.wordTextview.text = it?.word ?: "단어 선택 필요"
+ binding.meaningTextview.text = it?.meaning ?: ""
+ }
+
+ binding.floatingButton.setOnClickListener {
+ startActivity(Intent(this, AddEditActivity::class.java))
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt
new file mode 100644
index 0000000..3b83e9b
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordAdapter.kt
@@ -0,0 +1,35 @@
+package com.example.bcsd_android_2025_1
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.bcsd_android_2025_1.databinding.ItemWordBinding
+
+class WordAdapter (
+ private val onTopClick: (WordListData) ->Unit
+) : ListAdapter(DiffCallback()){
+
+ inner class WordViewHolder(private val binding: ItemWordBinding): RecyclerView.ViewHolder(binding.root) {
+ fun bind(word: WordListData) {
+ binding.word = word
+ binding.root.setOnClickListener { onTopClick(word) }
+ binding.executePendingBindings()
+ }
+ }
+
+ override fun onCreateViewHolder(parent:ViewGroup, viewType: Int):WordViewHolder{
+ val binding = ItemWordBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return WordViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: WordViewHolder, position:Int){
+ holder.bind(getItem(position))
+ }
+
+ class DiffCallback: DiffUtil.ItemCallback(){
+ override fun areItemsTheSame(oldItem:WordListData, newItem:WordListData):Boolean = oldItem.id ==newItem.id
+ override fun areContentsTheSame(oldItem: WordListData, newItem: WordListData): Boolean = oldItem == newItem
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt
new file mode 100644
index 0000000..27eb462
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDao.kt
@@ -0,0 +1,20 @@
+package com.example.bcsd_android_2025_1
+
+import androidx.lifecycle.LiveData
+import androidx.room.*
+
+@Dao
+interface WordDao {
+ @Query("SELECT * FROM word_table ORDER BY id DESC")
+ fun getAllwords(): LiveData>
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insert(word: WordListData)
+
+ @Update
+ suspend fun update(word: WordListData)
+
+ @Delete
+ suspend fun delete(wordListData: WordListData)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt
new file mode 100644
index 0000000..17839db
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordDatabase.kt
@@ -0,0 +1,26 @@
+package com.example.bcsd_android_2025_1
+
+import android.content.Context
+import androidx.room.Room
+import androidx.room.Database
+import androidx.room.RoomDatabase
+
+@Database(entities = [WordListData::class], version = 1, exportSchema = false)
+abstract class WordDatabase : RoomDatabase() {
+ abstract fun wordDao(): WordDao
+
+ companion object{
+ @Volatile private var INSTANCE: WordDatabase?=null
+
+ fun getDatabase(context: Context): WordDatabase{
+ return INSTANCE ?: synchronized(this){
+ val instance = Room.databaseBuilder(
+ context.applicationContext, WordDatabase::class.java,
+ "word_database"
+ ).build()
+ INSTANCE = instance
+ instance
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordListData.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordListData.kt
new file mode 100644
index 0000000..76faa77
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordListData.kt
@@ -0,0 +1,12 @@
+package com.example.bcsd_android_2025_1
+
+import androidx.room.PrimaryKey
+import androidx.room.Entity
+
+@Entity(tableName = "word_table")
+data class WordListData (
+ @PrimaryKey(autoGenerate = true)
+ val id: Int = 0,
+ var word: String,
+ var meaning: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt
new file mode 100644
index 0000000..6235632
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordRepository.kt
@@ -0,0 +1,11 @@
+package com.example.bcsd_android_2025_1
+
+import androidx.lifecycle.LiveData
+
+class WordRepository(private val wordDao: WordDao) {
+ val allWords: LiveData> = wordDao.getAllwords()
+
+ suspend fun insert(word: WordListData) = wordDao.insert(word)
+ suspend fun update(word: WordListData) = wordDao.update(word)
+ suspend fun delete(word: WordListData) = wordDao.delete(word)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt
new file mode 100644
index 0000000..18d445c
--- /dev/null
+++ b/app/src/main/java/com/example/bcsd_android_2025_1/WordViewModel.kt
@@ -0,0 +1,24 @@
+package com.example.bcsd_android_2025_1
+
+import android.app.Application
+import androidx.lifecycle.*
+import kotlinx.coroutines.launch
+
+
+class WordViewModel(application: Application) : AndroidViewModel(application) {
+ private val repository: WordRepository
+ val allWords: LiveData>
+ private val _topWord = MutableLiveData()
+ val topWord: LiveData get() = _topWord
+
+ init {
+ val wordDao = WordDatabase.getDatabase(application).wordDao()
+ repository = WordRepository(wordDao)
+ allWords = repository.allWords
+ }
+
+ fun insert(word:WordListData) = viewModelScope.launch { repository.insert(word)}
+ fun update(word:WordListData) = viewModelScope.launch { repository.update(word)}
+ fun delete(word:WordListData) = viewModelScope.launch { repository.delete(word)}
+ fun setTopWord(word: WordListData) {_topWord.value = word}
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/delete_icon.png b/app/src/main/res/drawable/delete_icon.png
new file mode 100644
index 0000000..6f67376
Binary files /dev/null and b/app/src/main/res/drawable/delete_icon.png differ
diff --git a/app/src/main/res/drawable/edit_icon.png b/app/src/main/res/drawable/edit_icon.png
new file mode 100644
index 0000000..bb9a087
Binary files /dev/null and b/app/src/main/res/drawable/edit_icon.png differ
diff --git a/app/src/main/res/layout/activity_add_edit.xml b/app/src/main/res/layout/activity_add_edit.xml
new file mode 100644
index 0000000..0f40e66
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_edit.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 311f3cb..8dde162 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,19 +1,72 @@
-
+ xmlns:tools="http://schemas.android.com/tools">
+
-
+
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_word.xml b/app/src/main/res/layout/item_word.xml
new file mode 100644
index 0000000..720e158
--- /dev/null
+++ b/app/src/main/res/layout/item_word.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 c6c4daf..9ba87dc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,7 @@
BCSD_Android_2025-1
+ 추가
+ 단어
+ 뜻
+ 단어 추가
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 283fec9..ba0ef47 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -9,8 +9,15 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.9.3"
constraintlayout = "2.2.1"
+roomCommonJvm = "2.7.2"
+roomRuntimeAndroid = "2.7.2"
+lifecycle = "2.7.0"
+coroutines = "1.7.3"
+room = "2.7.2"
[libraries]
+androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
+kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
@@ -19,6 +26,10 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
+androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
+androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
+androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
+
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 6ddf3b7..f659462 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,5 +1,6 @@
pluginManagement {
repositories {
+ gradlePluginPortal()
google {
content {
includeGroupByRegex("com\\.android.*")