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 @@ + + + + + + + + + +