본문 바로가기

[TIL][내일배움캠프]

[내일배움캠프][TIL] 24.01.11 (목) - Android 앱개발 숙련 실습과제 4일차

1. Android 앱개발 숙련 실습과제 4일차

 

어제를 기점으로 필수 과제 항목들은 전부 완료하고, 선택 과제 항목들을 하기 시작했다.

그리고 세 개의 선택 과제 중 '플로팅 버튼' 부분은 부분적으로 완료하였다.

 

오늘은 나머지 두 가지를 진행할 예정이다.

 

 

선택 과제 - 상품 삭제하기

 

  • 상품을 롱클릭 했을때 삭제 여부를 묻는 다이얼로그를 띄우고
  • 확인을 선택시 해당 항목을 삭제하고 리스트를 업데이트한다.
  • 해당 상품이 삭제되었는지 확인!!

우선, 상품을 '롱클릭'했을 때 이벤트를 발생시켜야 하기 때문에 MyAdapter.kt에 인터페이스를 추가한다.

interface ItemLongClick {
	fun onLongClick(view: View, position: Int)
}
var itemLongClick : ItemLongClick? = null

 

그리고 MainActivity에 넘기기 위해서 다음 코드를 추가한다.

holder.itemView.setOnLongClickListener{
	itemLongClick?.onLongClick(it, position)
    return@setOnLongClickListener true
}

 

마지막으로, MainActivity.kt에는 롱클릭을 했을 때 다이얼로그를 실행시킬 수 있는 코드를 완성시킨다.

        adapter.itemLongClick = object : MyAdapter.ItemLongClick {
            override fun onLongClick(view: View, position: Int) {
                val delete = AlertDialog.Builder(this@MainActivity)
                delete.setIcon(R.drawable.chat)
                delete.setTitle("상품 삭제")
                delete.setMessage("상품을 정말로 삭제하시겠습니까?")
                delete.setPositiveButton("확인"){dialog, _ ->
                    dataList.removeAt(position)
                    adapter.notifyItemRemoved(position)
                }
                delete.setNegativeButton("취소"){dialog, _ ->
                    dialog.dismiss()
                }

                delete.show()
            }
        }

 

그 결과는 다음과 같다.

 

 

 

선택 과제 - 좋아요 기능

 

  • 상품 상세 화면에서 좋아요 선택시 아이콘 변경 및 Snackbar 메세지 표시
  • 메인 화면으로 돌아오면 해당 상품에 좋아요 표시 및 좋아요 카운트 +1
  • 상세 화면에서 좋아요 해제시 이전 상태로 되돌림

이번 선택 과제는 다른 두 선택 과제에 비해서 그 난이도가 훨씬 높다. (얼마나 어려우면 설명할 때부터도 찐 도전과제라고 붙였을까)

 

그래서 이번 선택 과제는 유일하게 해설의 도움을 받았다.

 

 

좋아요 기능을 만드는 과정은 굉장히 복잡하기 그지없다.

위의 메커니즘을 간단히 요약하면 다음과 같다.

 

  • 메인 화면에서는 좋아요를 눌러도 오르지 않는다.
  • 상세 화면에서 좋아요 버튼을 누르면 메인 화면의 좋아요 수가 1 올라간다. (해제하면 다시 1이 내려간다)

즉, 여기에서는 예전에 배웠던 registerForActivityResult를 사용해야 한다.

그 외에 좋아요일 때와 아닐 때 이 두 가지를 구분할 수 있는 변수형인 Boolean을 사용하는 것이 좋다.

 

이제 기본적인 설명은 끝났고 한 번 만들어보도록 하자.

 

우선, Data Class에 Boolean 자료형인 isGood 변수를 추가한다.

그리고 모든 dataList에 기본값인 False를 기입한다.

 

이제 MyAdapter.kt에 추가할 것들을 살펴보자.

일단, 좋아요 버튼을 눌렀을 때는 빨간 하트, 안 눌렀을 때는 텅 빈 하트를 넣어야하는데 현재 기본값이 False이므로, True일 때 빨간 하트를 넣어야 한다.

if(mItems[position].isGood){
	holder.ivGood.setImageResource(R.drawable.red_heart)
} else {
	holder.ivGood.setImageResource(R.drawable.heart)
}

 

그리고 inner Class인 holder에도 하트 아이콘을 추가한다.

val ivGood = binding.ivGood

 

 

그 다음부터는 MainActivity와 DetailActivity를 설명해야하는데 왔다갔다하면 코드가 굉장히 난잡해질 수 있으니 우선은 액티비티 별로 추가되는 코드를 적고 설명하겠다.

 

MainActivity.kt

//activityResultLauncher 변수 선언
lateinit var activityResultLauncher: ActivityResultLauncher<Intent>

//registerForActivityResult 선언
        activityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == RESULT_OK) {
                val goodIndex = it.data?.getIntExtra("goodIndex", 0) as Int
                val isGood = it.data?.getBooleanExtra("isGood", false) as Boolean

                if (isGood) {
                    dataList[goodIndex].isGood = true
                    dataList[goodIndex].aGood += 1
                } else {
                    if (dataList[goodIndex].isGood) {
                        dataList[goodIndex].isGood = false
                        dataList[goodIndex].aGood -= 1
                    }
                }

                adapter.notifyItemChanged(goodIndex)
            }
        }
        
// DetailActivity에 activityResultLauncher 쓰기
        adapter.itemClick = object : MyAdapter.ItemClick {
            override fun onClick(view: View, position: Int) {

                val intent = Intent(this@MainActivity, DetailActivity::class.java).apply{
                    putExtra(Constants.ITEM_OBJECT, dataList[position])
                    putExtra(Constants.ITEM_INDEX, position)
                }
                activityResultLauncher.launch(intent)
            }
        }

 

DetailActivity.kt

        isGood = dataList?.isGood == true
        binding.ivIsgood.setImageResource(if(isGood) {R.drawable.red_heart} else {R.drawable.heart})

        binding.ivIsgood.setOnClickListener {
            if(!isGood) {
                binding.ivIsgood.setImageResource(R.drawable.red_heart)
                Snackbar.make(binding.constraintMain, "관심 목록에 추가되었습니다.", Snackbar.LENGTH_SHORT).show()
                isGood = true
            } else {
                binding.ivIsgood.setImageResource(R.drawable.heart)
                isGood = false
            }
        }

        binding.ivBack.setOnClickListener {
            val intent = Intent(this, MainActivity::class.java).apply{
                putExtra("goodIndex", itemPosition)
                putExtra("isGood", isGood)
            }

            setResult(RESULT_OK, intent)
            if(!isFinishing) finish()
        }

 

 

그리고 이렇게 완성시키고 나서 돌린 결과는…?

 

 

트러블슈팅 & 피드백

 

1. 트러블슈팅

  • 보기좋게 앱 실행이 오류가 난다. 상세 화면으로 넘어가려 하면 앱이 자동으로 꺼지거나, 상세 화면에서 뒤로가기 버튼을 눌러서 메인 화면으로 넘어가면 검은 화면으로 바뀌거나.

 

2. 피드백

  • 역시나 기능을 하나 추가한다는 것이 여간 어려운 일이 아니다. 남들이 보기에는 그냥 기능 하나 추가하는 거지만, 개발자의 입장에서는 이거 하나 추가하려다가 기존에 만든 코드들이 전부 무용지물이 되는 불상사가 생기기 때문. 앞으로 게임 패치 할 때마다 오류가 시도때도 없이 생긴다고 뭐라 하지 말아야겠다.