๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿ“ฑ/๐Ÿ“˜Project

[Kotlin] Note ์•ฑ ๋งŒ๋“ค๊ธฐ๏ผ“

Room์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด ์ค˜์•ผ ํ•จ.

plugins์™€ dependencies์— ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ์คŒ.

build.gradle (module)
plugins {
    ...
    id 'kotlin-kapt'
}

android {
    ...
}

dependencies {
	...
    // room
    def roomVersion = "2.4.3"

    implementation "androidx.room:room-ktx:$roomVersion"
    kapt "androidx.room:room-compiler:$roomVersion"
}

 

Room์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” 3๊ฐœ์˜ ํŒŒ์ผ(Dao, Database, Entity)์„ ๋งŒ๋“ค์–ด ์ค˜์•ผ ํ•จ.

Entity๋Š” ์ „์— ์šฐ๋ฆฌ๊ฐ€ ์ผ๋˜ NoteEntity ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ์“ฐ๊ณ  ์šฉ๋„๋Š” ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค,

Dao๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€, ์ˆ˜์ •, ์‚ญ์ œ ๋“ฑ์„ ์œ„ํ•œ ์ฟผ๋ฆฌ๋ฌธ ์ž‘์„ฑ์— ์“ฐ์ผ ๊ฒƒ์ด๊ณ 

Database๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ์—ญํ• ์„ ํ•จ.

 

๋‚˜๋Š” Room ํŒŒ์ผ๋“ค์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์‰ฝ๊ฒŒ room ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์–ด์คฌ์Œ.

 

 

NoteEntity.kt
package com.ta2gi.note.room

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

// tableName์€ ํ…Œ์ด๋ธ”๋ช…์ด๋ฉฐ ์•ˆ์“ด๋‹ค๋ฉด ํด๋ž˜์Šค๋ช…์ด ํ…Œ์ด๋ธ”๋ช…์ด ๋จ
@Entity(tableName = "NoteTable")
data class NoteEntity(
    // ๊ณ ์œ  ํ‚ค ๊ฐ’, autoGenerate๋Š” ์ž๋™์œผ๋กœ ์ฆ๊ฐ€์‹œํ‚ด
    @PrimaryKey(autoGenerate = true)
    val uid : Int?,
    // ColumnInfo์€ ์ปฌ๋Ÿผ๋ช…์„ ๋ณ€์ˆ˜๋ช… ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•จ, ๋”ฐ๋กœ ์ด๋ฆ„์„ ๋ถ™์ผ์ˆ˜๋„ ์žˆ์Œ
    @ColumnInfo
    val note : String,
    @ColumnInfo
    val date : String,
    @ColumnInfo
    val color : String
)

 

NoteDao.kt
package com.ta2gi.note.room

import androidx.room.*

@Dao
interface NoteDao {
    // ๋…ธํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
    @Query("SELECT * FROM NoteTable")
    fun getNote() : MutableList<NoteEntity>

    // ๋…ธํŠธ ์ถ”๊ฐ€
    @Insert
    fun insertNote(note : NoteEntity)

    // ๋…ธํŠธ ์ˆ˜์ •
    @Update
    fun updateNote(note : NoteEntity)
}

 

NoteDatabase.kt
package com.ta2gi.note.room

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [NoteEntity::class], version = 1)
abstract class NoteDatabase : RoomDatabase() {
    abstract fun noteDao() : NoteDao

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋”ฑ ํ•œ๋ฒˆ๋งŒ ์ƒ์„ฑํ•˜๊ฒ ๋‹ค๋Š” ์ฝ”๋“œ
    companion object {
        private var instance : NoteDatabase? = null

        @Synchronized
        fun getInstance(context: Context): NoteDatabase? {
            if (instance == null) {
                synchronized(NoteDatabase::class) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        NoteDatabase::class.java,
                        "note.db"
                    ).build()
                }
            }

            return instance
        }
    }
}

 

์ด์ œ ๋ฉ”๋ชจ ์ถ”๊ฐ€, ์ˆ˜์ • ๋ถ€๋ถ„์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด ๋ด„.

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋• ์“ฐ๋ ˆ๋“œ๋ฅผ ์จ์•ผ ํ•จ.

 

์—ฌ๊ธฐ๊นŒ์ง€์˜ ์ฝ”๋“œ๋“ค์„ ์˜ฌ๋ฆฌ๊ฒ ์Œ.

(๋ถ€๋ถ„ ๋ถ€๋ถ„ ์ˆ˜์ •ํ•œ ๊ณณ์ด ๋งŽ์•„ ๋‹ค์‹œ ์˜ฌ๋ฆฌ๋Š” ๊ฒ๋‹ˆ๋‹ค..)

 

MainActivity.kt
package com.ta2gi.note

import android.app.Activity
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.ta2gi.note.databinding.ActivityMainBinding
import com.ta2gi.note.fragment.AddFragment
import com.ta2gi.note.fragment.DetailFragment
import com.ta2gi.note.fragment.EditFragment
import com.ta2gi.note.fragment.HomeFragment
import com.ta2gi.note.room.NoteDatabase
import com.ta2gi.note.room.NoteEntity

class MainActivity : AppCompatActivity() {

    lateinit var mainBinding: ActivityMainBinding

    // ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๋‹ด์„ ๋ณ€์ˆ˜
    lateinit var currentFragment: Fragment

    // ๋…ธํŠธ ๋ฆฌ์ŠคํŠธ
    val noteList = mutableListOf<NoteEntity>()

    // ์–ด๋Žํ„ฐ
    val noteAdapter = NoteAdapter(this, noteList)

    // ๋…ธํŠธ ์ƒ‰์ƒ ์นด์šดํŠธ
    var noteCount = 0

    // ํด๋ฆญํ•œ ๋…ธํŠธ ์ •๋ณด
    var noteText : String? = null
    var noteDate : String? = null
    var noteColor : String? = null
    var noteUid : Int? = null

    // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
    lateinit var noteDatabase : NoteDatabase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mainBinding.root)

        // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ
        noteDatabase = NoteDatabase.getInstance(this)!!

        // ์ดˆ๊ธฐํ™”๋ฉด
        fragmentController("home", add = false)
    }

    // ํ”„๋ž˜๊ทธ๋จผํŠธ
    fun fragmentController(view: String, add: Boolean) {
        // ๋“ค์–ด์˜ค๋Š” view ๊ฐ’์— ๋”ฐ๋ผ ํ”„๋ž˜๊ทธ๋จผํŠธ ๋ณ€๊ฒฝ
        when (view) {
            "home" -> currentFragment = HomeFragment(this)
            "add" -> currentFragment = AddFragment(this)
            "detail" -> currentFragment = DetailFragment(this)
            "edit" -> currentFragment = EditFragment(this)
        }

        // ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ต์ฒด ํ•ด์ฃผ๊ธฐ
        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.replace(R.id.mai_screen, currentFragment)

        // ๋ฐฑ์Šคํ…์— ์ถ”๊ฐ€
        if (add) fragmentTransaction.addToBackStack(view)

        // ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ ๋„ฃ๊ธฐ
        fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)

        // ํ”„๋ž˜๊ทธ๋จผํŠธ ์‹คํ–‰
        fragmentTransaction.commit()
    }

    // ํ‚ค๋ณด๋“œ ๋‚ด๋ฆฌ๊ธฐ
    fun hideKeyboard(act : Activity){
        val imm = act.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(act.currentFocus?.windowToken, 0)
    }

    // ๋…ธํŠธ ์ƒ‰์ƒ
    fun noteColor() : String {
        var noteColor = ""

        // 7์ด ๋˜๋ฉด 0๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘
        if(noteCount == 7) noteCount = 0

        when (noteCount) {
            // ๋นจ, ์ฃผ, ๋…ธ, ์ดˆ, ํŒŒ, ๋‚จ, ๋ณด
            0 -> noteColor = "#E53935"
            1 -> noteColor = "#F4511E"
            2 -> noteColor = "#FFB300"
            3 -> noteColor = "#00897B"
            4 -> noteColor = "#1E88E5"
            5 -> noteColor = "#3949AB"
            6 -> noteColor = "#5E35B1"
        }
        noteCount++

        return noteColor
    }
}

 

HomeFragment.kt
package com.ta2gi.note.fragment

import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ta2gi.note.MainActivity
import com.ta2gi.note.R
import com.ta2gi.note.databinding.FragmentHomeBinding
import kotlin.concurrent.thread

class HomeFragment(val mainActivity : MainActivity) : Fragment(), View.OnClickListener {

    lateinit var homeBinding : FragmentHomeBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        homeBinding = FragmentHomeBinding.inflate(inflater)

        // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ ๋œ ๋…ธํŠธ๋“ค ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ ํ›„ ์–ด๋Žํ„ฐ์— ์•Œ๋ฆฌ๊ธฐ
        thread {
            mainActivity.noteList.clear()
            mainActivity.noteList.addAll(mainActivity.noteDatabase.noteDao().getNote())

            // ์–ด๋Žํ„ฐ ๋ผ์šฐ๊ธฐ
            homeBinding.homRecyclerview.adapter = mainActivity.noteAdapter

            mainActivity.noteAdapter.notifyDataSetChanged()
        }

        // ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ๋Š” ์นœ๊ตฌ๋“ค์€ setOnClickListener๋ฅผ ๋‹ฌ์•„์ค˜์•ผ ํ•จ
        homeBinding.homAdd.setOnClickListener(this)

        return homeBinding.root
    }

    override fun onClick(view : View?) {
        when(view) {
            // ์ถ”๊ฐ€ ๋ฒ„ํŠผ
            // +๋ฅผ ๋ˆ„๋ฅด๋ฉด AddFragment๋กœ ์ด๋™
            homeBinding.homAdd -> mainActivity.fragmentController("add", true)
        }
    }
}

 

AddFragment.kt
package com.ta2gi.note.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ta2gi.note.MainActivity
import com.ta2gi.note.room.NoteEntity
import com.ta2gi.note.databinding.FragmentAddBinding
import java.text.SimpleDateFormat
import java.util.*
import kotlin.concurrent.thread

class AddFragment(val mainActivity : MainActivity) : Fragment(), View.OnClickListener {

    lateinit var addBinding : FragmentAddBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        addBinding = FragmentAddBinding.inflate(inflater)

        addBinding.addCancel.setOnClickListener(this)
        addBinding.addComplete.setOnClickListener(this)

        return addBinding.root
    }

    override fun onClick(view : View?) {
        when(view) {
            // ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ
            // ์ด์ „์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋กœ ๋Œ์•„๊ฐ€๊ธฐ
            addBinding.addCancel -> mainActivity.supportFragmentManager.popBackStack()

            // ์™„๋ฃŒ ๋ฒ„ํŠผ
            addBinding.addComplete -> {
                // ํ…์ŠคํŠธ์— ์ ํžŒ ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ
                val editText = addBinding.addEdittext.text.toString()

                // ์˜ค๋Š˜ ๋‚ ์งœ ๊ฐ€์ ธ์˜ค๊ธฐ(์ž‘์„ฑ์ผ)
                val sdf = SimpleDateFormat("yyyy.MM.dd", Locale.KOREAN)
                val date =  sdf.format(System.currentTimeMillis())

                // ๋…ธํŠธ ์ƒ‰์ƒ ์ •ํ•˜๊ธฐ
                val noteColor = mainActivity.noteColor()

                // ๋…ธํŠธ ์ •๋ณด ๊ฐ์ฒด์— ๋„ฃ๊ธฐ
                val entity = NoteEntity(null, editText, date, noteColor)
                // ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋…ธํŠธ ์ถ”๊ฐ€
                thread {
                    mainActivity.noteDatabase.noteDao().insertNote(entity)

                    // ์–ด๋Žํ„ฐ์— ๋ฆฌ์ŠคํŠธ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์•Œ๋ ค์ฃผ๊ณ  ํ‚ค๋ณด๋“œ ๋‚ด๋ฆฌ๊ธฐ
                    mainActivity.noteAdapter.notifyDataSetChanged()
                    mainActivity.hideKeyboard(mainActivity)

                    mainActivity.supportFragmentManager.popBackStack()
                }
            }
        }
    }
}

 

DetailFragment.kt
package com.ta2gi.note.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ta2gi.note.MainActivity
import com.ta2gi.note.R
import com.ta2gi.note.databinding.FragmentDetailBinding

class DetailFragment(val mainActivity : MainActivity) : Fragment(), View.OnClickListener {

    lateinit var detailBinding: FragmentDetailBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        detailBinding = FragmentDetailBinding.inflate(inflater)

        detailBinding.detTitle.text = mainActivity.noteDate
        detailBinding.detText.text = mainActivity.noteText

        detailBinding.detCancel.setOnClickListener(this)
        detailBinding.detEdit.setOnClickListener(this)

        return detailBinding.root
    }

    override fun onClick(view : View?) {
        when(view) {
            // ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ
            detailBinding.detCancel -> {
                // ํ™ˆ ํ™”๋ฉด์œผ๋กœ ๋Œ์•„๊ฐ€๋ฉด์„œ ํด๋ฆญํ–ˆ๋˜ ๋…ธํŠธ์˜ ์ •๋ณด๋ฅผ ์ดˆ๊ธฐํ™”
                mainActivity.noteUid = null
                mainActivity.noteText = null
                mainActivity.noteDate = null
                mainActivity.noteColor = null

                mainActivity.supportFragmentManager.popBackStack()
            }

            // ์ˆ˜์ •ํ•˜๊ธฐ ๋ฒ„ํŠผ
            detailBinding.detEdit -> mainActivity.fragmentController("edit", true)
        }
    }

    // ์ˆ˜์ •ํ•˜๊ณ  ๋Œ์•„์˜จ ํ›„ ์ˆ˜์ •๋œ ํ…์ŠคํŠธ๋กœ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•œ ์šฉ๋„
    override fun onResume() {
        super.onResume()

        detailBinding.detText.text = mainActivity.noteText
    }
}

 

EditFragment.kt
package com.ta2gi.note.fragment

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ta2gi.note.MainActivity
import com.ta2gi.note.room.NoteEntity
import com.ta2gi.note.databinding.FragmentEditBinding
import kotlin.concurrent.thread

class EditFragment(val mainActivity : MainActivity) : Fragment(), View.OnClickListener {

    lateinit var editBinding : FragmentEditBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        editBinding = FragmentEditBinding.inflate(inflater)

        // ์ˆ˜์ •ํ•  ํ…์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
        editBinding.ediEdittext.setText(mainActivity.noteText)

        editBinding.ediCancel.setOnClickListener(this)
        editBinding.ediComplete.setOnClickListener(this)

        return editBinding.root
    }

    override fun onClick(view : View?) {
        when(view) {
            // ๋Œ์•„๊ฐ€๊ธฐ ๋ฒ„ํŠผ
            editBinding.ediCancel -> mainActivity.supportFragmentManager.popBackStack()

            // ์™„๋ฃŒ ๋ฒ„ํŠผ
            editBinding.ediComplete -> {
                // ํ…์ŠคํŠธ์— ์ ํžŒ ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ
                val editText = editBinding.ediEdittext.text.toString()

                // ์ˆ˜์ •ํ•œ ๋…ธํŠธ ์ •๋ณด ๊ฐ์ฒด์— ๋„ฃ๊ธฐ
                val entity = NoteEntity(mainActivity.noteUid!!, editText, mainActivity.noteDate!!, mainActivity.noteColor!!)
                thread {
                    mainActivity.noteDatabase.noteDao().updateNote(entity)

                    // ์™„๋ฃŒํ•˜๊ณ  ๋Œ์•„๊ฐˆ ๋•Œ ํ”„๋ž˜๊ทธ๋จผํŠธ ํ…์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•œ ํ…์ŠคํŠธ๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ
                    mainActivity.noteText = editText

                    // ์–ด๋Žํ„ฐ์— ๋ฆฌ์ŠคํŠธ ๋ณ€๊ฒฝ ์‚ฌํ•ญ ์•Œ๋ ค์ฃผ๊ณ  ํ‚ค๋ณด๋“œ ๋‚ด๋ฆฌ๊ธฐ
                    mainActivity.noteAdapter.notifyDataSetChanged()
                    mainActivity.hideKeyboard(mainActivity)

                    mainActivity.supportFragmentManager.popBackStack()
                }
            }
        }
    }
}

 

NoteAdapter.kt
package com.ta2gi.note

import android.content.res.ColorStateList
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.ta2gi.note.room.NoteEntity

class NoteAdapter(val mainActivity : MainActivity, val noteList : MutableList<NoteEntity>) : RecyclerView.Adapter<NoteAdapter.ViewHolderClass>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteAdapter.ViewHolderClass {
        // ํ•ญ๋ชฉ์˜ layout์€ R.layout.list_item
        val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)

        return ViewHolderClass(view)
    }

    override fun onBindViewHolder(holder: NoteAdapter.ViewHolderClass, position: Int) {
        // ๋…ธํŠธ ๋‚ด์šฉ ๋„ฃ๊ธฐ
        holder.note.text = noteList[position].note
        // ๋…ธํŠธ ์ƒ‰์ƒ ๋„ฃ๊ธฐ
        holder.background.backgroundTintList = ColorStateList.valueOf(Color.parseColor(noteList[position].color))

        holder.note.setOnClickListener {
            // ํด๋ฆญํ•œ ๋…ธํŠธ ์ •๋ณด ๋‹ด๊ณ  ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™
            mainActivity.noteUid = noteList[position].uid
            mainActivity.noteText = holder.note.text.toString()
            mainActivity.noteDate = noteList[position].date
            mainActivity.noteColor = noteList[position].color
            mainActivity.fragmentController("detail", true)
        }
    }

    override fun getItemCount(): Int {
        // ๋ฆฌ์ŠคํŠธ์— ๋‹ด๊ธด ํ•ญ๋ชฉ์˜ ํฌ๊ธฐ๋งŒํผ ๋‚˜์˜ค๊ฒŒํ•˜๊ธฐ
        return noteList.size
    }

    inner class ViewHolderClass(view : View) : RecyclerView.ViewHolder(view) {
        val background = view.findViewById<LinearLayout>(R.id.ite_layout)
        val note = view.findViewById<TextView>(R.id.ite_text)
    }
}

 

์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ํ•˜๋ฉด ๋ชจ๋“ ๊ฒŒ ์ž˜ ๋Œ์•„๊ฐ.

๋‹ค์Œ ๊ธ€์—์„œ๋Š” ์‚ญ์ œ ๊ธฐ๋Šฅ์„ ๋„ฃ์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Œ.

 

To be Continued..๐Ÿง