[Android] 8(2). 데이터베이스와 소스 코드 연결하기

3 minute read


데이터베이스와 소스 코드 연결하기

이번 포스팅은 지난 “8(2). 관계형 데이터베이스 SQLite” 포스팅에 이어서 작성됩니다.


화면 만들기


리사이클러 뷰를 사용해서 데이터베이스에 있는 데이터들이 출력되도록 화면을 구성합니다.


activity_main.xml

image-20210819222000957

  • 리사이클러 뷰: id=recyclerMemo
  • 플레인 텍스트: id=editMemo, inputType=textMultiLine, hint: 메모를 입력하세요
  • 버튼: id=btnSave


item_recycler.xml

image-20210819222203173

  • 번호 텍스트 뷰: id=textNo, text=01
  • 내용 텍스트 뷰: id=textContent, gravity=center_vertical, (maxLines, ellipsize)
  • 날짜 텍스트 뷰: id=textDatetime
  • 삭제 버튼: id=btnDelete



소스 코드 연결하기


리사이클러 뷰를 만들고 연결하는 과정은 [Android] 5(2). 컨테이너 (목록 만들기) 에서 상세히 설명했으니 여기서는 데이터베이스와 연결함으로써 달라지는 부분 위주로 설명합니다.

데이터베이스와 리사이클러 뷰를 연결하여 추가와 삭제 기능을 구현하겠습니다.


RecyclerAdapter.kt

// 어댑터 클래스
class RecyclerAdapter: RecyclerView.Adapter<RecyclerAdapter.Holder>() {

    // 삭제 기능 추가 2-1: SqliteHelper 프로퍼티
    var helper: SqliteHelper? = null

    var listData = mutableListOf<Memo>()

    // 삭제 기능 추가: 3. 뷰 홀더 클래스 -> 데이터 삭제 시 어댑터의 helper와 listData 프로퍼티에 접근하기 위해 어댑터 클래스 안에 정의
    inner class Holder(val binding: ItemRecyclerBinding): RecyclerView.ViewHolder(binding.root){
        // 삭제 기능 추가: 5-1. setMemo 메서드로 넘어온 Memo 임시 저장
        var mMemo: Memo? = null

        // 삭제 기능 추가 4: init 초기화 블록 안에 클릭 리스너 생성
        init{
            binding.btnDelete.setOnClickListener {
                // 삭제 기능 추가 6: 데이터베이스와 리사이클러 뷰의 데이터 삭제
                // SQLite의 데이터를 먼저 삭제하고 listData 데이터를 삭제
                helper?.deleteMemo(mMemo!!)
                listData.remove(mMemo)
                notifyDataSetChanged()
            }
        }


        fun setMemo(memo: Memo){
            // 삭제 기능 추가: 5-2. setMemo 메서드로 넘어온 Memo 임시 저장
            this.mMemo = memo

            binding.textNo.text = "${memo.no}"
            binding.textContent.text = memo.content
            val sdf = SimpleDateFormat("yyyy/MM/dd hh:mm")
            binding.textDatetime.text = "${sdf.format(memo.datetime)}"
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemRecyclerBinding.inflate(
            LayoutInflater.from(parent.context), parent, false)

        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val memo = listData.get(position)
        holder.setMemo(memo)
    }

    override fun getItemCount(): Int {
        return listData.size
    }
}

리사이클러 뷰를 사용하려면 어댑터 클래스와 뷰 홀더 클래스를 생성해야 합니다.

삽입 기능을 구현할 때는 RecyclerAdapter 클래스의 코드에 수정이 필요하지 않습니다. MainActivity.kt 파일에만 코드 작성이 이루어집니다.

삭제 기능을 구현하기 위해서 RecyclerAdapter 클래스의 코드에 몇가지 수정을 해야 합니다. 위 소스코드에서 순서대로 설명을 했지만, 간단하게만 짚고 넘어가겠습니다.

  1. 아이템 레이아웃에 삭제 버튼을 배치합니다. (화면 만들기 부분에서 완료했습니다)
  2. 어댑터 클래스 내에 SqliteHelper 프로퍼티를 생성합니다.
  3. 삭제 기능을 위해서는 뷰 홀더 클래스에서 어댑터 클래스의 데이터베이스와 화면 목록 프로퍼티의 참조가 필요합니다. 삭제 버튼의 클릭 리스너는 뷰 홀더 클래스 내에 만들어지기 때문에, 어댑터 클래스의 프로퍼티들을 참조하기 위해서는 뷰 홀더 클래스를 어댑터 클래스 안에 정의해야 합니다.
    • RecyclerView.Adapter의 제네릭 타입으로 지정
    • 뷰 홀더 클래스 앞에 inner class 선언
  4. Holder 클래스의 init 블록 내에서 삭제 버튼의 클릭 리스너를 달아줍니다.
  5. 홀더는 한 화면에 그려지는 개수만큼 만든 후 재사용하므로 1번 메모가 있는 홀더를 스크롤해서 위로 올리면 아래에서 올라오는 새로운 메모가 1번 홀더를 재사용하는 구조입니다. 따라서 삭제 버튼을 클릭하는 시점에 어떤 데이터인지 알아야 하므로 Holder 클래스의 init 위에 변수를 하나 선언하고 setMemo( ) 메서드로 넘어온 Memo를 임시 저장합니다.
  6. 클릭 리스너 안에서 데이터를 삭제하는 코드를 추가합니다. 데이터를 삭제할 때는 데이터베이스의 데이터를 먼저 삭제한 후에 화면 목록의 데이터를 삭제합니다. 리사이클러 뷰의 아이템 목록이 바뀌면 항상 notifyDataSetChanged( ) 메서드를 호출해야 합니다.


MainActivity.kt

MainActivity.kt 파일에서 삭제와 관련된 코드로는 데이터베이스 인스턴스를 생성하고 이를 어댑터 클래스의 helper 프로퍼티에 설정하는 것이 전부입니다.

MainActivity.kt 파일에서는 데이터베이스를 생성하고, 어댑터를 설정하고, 데이터 삽입과 관련된 코드들을 추가합니다.

class MainActivity : AppCompatActivity() {

    val binding by lazy {ActivityMainBinding.inflate(layoutInflater)}

    // SqliteHelper 인스턴스를 생성
    val helper = SqliteHelper(this, "memo", 1)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        // 리사이클러 뷰 어댑터 생성
        val adapter = RecyclerAdapter()
        // 삭제 기능 추가: 2-2. 어댑터에 SqliteHelper 전달
        adapter.helper = helper

        // 어댑터의 listData에 데이터베이스에서 가져온 데이터 세팅
        adapter.listData.addAll(helper.selectMemo())

        // 레이아웃의 리사이클러뷰 위젯에 어댑터를 연결하고 레이아웃 매니저 설정
        binding.recyclerMemo.adapter = adapter
        binding.recyclerMemo.layoutManager = LinearLayoutManager(this)

        // 저장 버튼에 클릭 리스너 달기
        binding.btnSave.setOnClickListener {
            // 플레인 텍스트에 입력된 내용이 있으면,
            if(binding.editMemo.text.toString().isNotEmpty()) {
                // 데이터베이스에 INSERT
                val memo = Memo(null, binding.editMemo.text.toString(), System.currentTimeMillis())
                helper.insertMemo(memo)
                // 데이터베이스 데이터가 변하면 리사이클러뷰에 업데이트
                adapter.listData.clear()
                adapter.listData.addAll(helper.selectMemo())
                adapter.notifyDataSetChanged()
                // 플레인 텍스트 초기화
                binding.editMemo.setText("")
            }
        }
    }
}

삽입과 관련된 코드만 살펴보겠습니다.

  • 저장 버튼에 클릭 리스너를 달아줍니다.
  • 저장 버튼을 클릭했을 때 메모 내용을 입력하는 플레인 텍스트에 내용이 있다면, 데이터 삽입을 진행합니다.
  • Memo 인스턴스를 생성하고 데이터베이스에 먼저 삽입합니다.
  • 데이터베이스 삽입 후에 리사이클러 뷰 목록에 삽입을 진행합니다. 리사이클러 뷰 목록에 삽입을 할 때는 목록 내의 데이터들을 모두 삭제(clear)하고, 데이터베이스에 있는 모든 데이터를 가져와 목록에 추가한 다음, 목록에 변화가 있으므로 notifyDataSetChanged() 메서드를 호출합니다.
  • 삽입을 완료했으면 플레인 텍스트의 내용을 초기화합니다.



결과 화면


image-20210819225906063

4개의 데이터를 추가하고 1번 데이터를 삭제한 후의 모습입니다.



정리


  • 데이터를 삽입, 삭제, 수정할 때는 데이터베이스에 먼저 적용하고 이후에 리사이클러 뷰 목록에 적용합니다.
  • 데이터의 삽입은 메인 액티비티에서 구현합니다.
  • 데이터의 삭제는 뷰 홀더 클래스 내에서 구현합니다.
  • 리사이클러 뷰 목록에 변화가 생기면 반드시 notifyDataSetChanged( ) 메서드를 호출해야 합니다.

Categories:

Updated:

Leave a comment