본문 바로가기

TIL

[TIL] 뒤로가기 버튼 클릭 이벤트 | putExtra, getParcelableExtra로 데이터 옮기기

[오늘 배운 내용]

-1- Android 뒤로가기 버튼 클릭 이벤트

  • 기존의 onBackPressed() 가 deprecated 되어서 OnBackPressedDIspatcher 를 통해 뒤로가기 버튼 클릭 이벤트를 생성 해야 한다.
  • Fragment에서의 뒤로가기 버튼 클릭 시 콜백은 해당 Fragment가 최소한 Started일 때만 호출된다.

 

 

  • 뒤로가기 버튼 클릭시 다이얼로그 통해 앱 종료되는 이벤트
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 앱 종료 다이얼로그창 생성
        fun exitShowDialog() {
            val builder = AlertDialog.Builder(this)
            builder.setTitle("앱 종료")
                .setMessage("앱을 종료하시겠습니까?")
                .setIcon(R.drawable.baseline_android_24)
                .setPositiveButton("앱 종료") { dialogInterface: DialogInterface, i: Int -> finish() }
                .setNegativeButton("취소") { dialogInterface: DialogInterface, i: Int ->
                    dialogInterface.dismiss()
                }
            builder.show()
        }
        // callback 인스턴스 생성
        val callback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                exitShowDialog()
            }
        }
        // 미리 생성해둔 callback 인스턴스 적용
        this.onBackPressedDispatcher.addCallback(this, callback)

 

-2- RecyclerView에 있는 Item을 Parcelize 이용해서 데이터 옮겨 DetailPage로 이동하기.

  • RecyclerView 에 있는 특정 Item 클릭 시 해당 아이템의 Data를 확인할 수 있는 DetailActivity로 이동하는 기능을 추가해보자.

 

 

먼저 activity_detail.xml 을 생성하자

지금 실습하고 있는 UI 예제...

 

 

 

Adapter.kt

  • xml 파일 생성이 완료 되었으면 RecyclerView의 Adapter Class로 이동해서 Interface로 OnClick 기능을 만들어주자.
class MyAdapter (private val mItems: MutableList<ItemData>) : RecyclerView.Adapter<ViewHolder>(){

	// 아이템 클릭 이벤트 인터페이스 생성
    interface ItemClick {
        fun onClick(view: View, position: Int)
    }

    var itemClick: ItemClick? = null


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


	// onBindViewHolder에 Click이벤트 적용
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.setOnClickListener {
            itemClick?.onClick(it, position)
        }
        holder.itemImage.setImageResource(mItems[position].mItemImage)
        holder.itemImage.clipToOutline=true // 이미지 크기 조정
        ...
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemViewBinding.inflate(LayoutInflater.from(parent.context),parent, false)
        return ViewHolder(binding)
    }
}


class ViewHolder(binding: ItemViewBinding) : RecyclerView.ViewHolder(binding.root){
    val itemImage = binding.imageviewItem
    val itemName = binding.tvItemName
    ...
}
  • Adapter에 클릭리스너를 생성하고 난 뒤, 데이터를 옮겨줄 Parcelable 을 적용하자. Parcel, Parcelable 은 -3- 에 잘 정리된 블로그 주소를 남겨놨음.

 

 

 

 

 

  • Parcelize 적용하는 방법
    • 1. build.gradle(module : app) 의 plugins {} 내부에 해당 코드 추가 후 sync now
    • 2. Data.kt 에서 Parcelize 적용

 

build.gradle.kts

// 플러그인은 build.gradle 상단에 있다.
plugins {
    ...
    id("kotlin-parcelize")
}

 

Data.kt

  • data class 앞에 @Parselize 추가
  • data class () 뒤에 : Parcelable 추가
@Parcelize
data class ItemData(
    val mItemImage : Int,
    val mItemName : String,
    val mItemAddress : String,
    val mItemPrice : Int,
    val mItemComment : Int,
    val mItemLike : Int
) : Parcelable

 

 

  • Parcelize를 다 적용시켜주었다면, RecyclerView와 Adapter로 연결되어 있는 Activity 로 돌아와서 Item클릭 시 해당 아이템의 Data를 넘겨주는 로직을 작성해보자
val myAdapter = MyAdapter(itemList)
        binding.recyclerView.adapter = myAdapter
        binding.recyclerView.layoutManager = LinearLayoutManager(this)
        binding.recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayout.VERTICAL))
        
        myAdapter.itemClick = object : MyAdapter.ItemClick {
            val context = binding.root.context
            override fun onClick(view: View, position: Int) {
                val intent = Intent(context, DetailActivity::class.java)
                intent.putExtra("data",itemList[position])
                intent.run { context.startActivity(this) }
            }
        }
  • Intent를 생성하려면 cotext가 필요한데, val context = binding.root.context 로 context를 가져올 수 있다.
  • intent = Intent(context, DetailActivity::class.java 를 통해 DetailActivity를 
  • Intent.putExtra를 통해 itemList 중에 내가 선택한 position의 데이터들을 "data" Extra에 담고, DetailActivity를 실행한다.

 

MainActivity에서 Data를 받아오고 받아온 "data"를 DetailActivity의 binding아이템에 적용시켜주면 된다.

class DetailActivity : AppCompatActivity() {
    private lateinit var binding: ActivityDetailBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityDetailBinding.inflate(layoutInflater)
        setContentView(binding.root)


        val data = intent.getParcelableExtra<ItemData>("data")

        binding.detailImageviewItem.setImageResource(data!!.mItemImage)
        binding.detailTvUserName.text = data?.mUserName
        binding.detailTvItemTradingAddress.text = data?.mItemAddress
        binding.detailTvItemName.text = data?.mItemName
        binding.detailTvItemPrice.text = MakeComma.makeComma(data?.mItemPrice)+"원"
        binding.detailTvItemDescription.text = data?.mItemDesc

    }
}

 

숫자 1000 단위마다 콤마 ( ' , ' ) 찍는 함수MakeComma를 adapter에서도 필요하고, DetailPage에도 필요해서 Object로 만들어놨다.

object MakeComma {
    fun makeComma(num:Int?):String{
        val formatter = DecimalFormat("###,###")
        return formatter.format(num)
    }
}

 

 

완성화면

 

 

-3- Parcel 

  • PutExtra 를 시도하다가 Parcel이란 게 나와서 간단하게 읽어 보았는데, putExtra를 할 때 필요한 직렬화를 할 때 Container 역할을 하는 클래스라고 한다.
  • 추후에 더 많은 기능 공부하며 필요성 느끼면 추가 예정
  • 자세한 설명은 하단 블로그에 잘 정리되어 있어서 참조하면 좋을 것 같다
  • https://kotlinworld.com/44
 

[Bundle] Android Parcel과 Parcelable 알아보기 + kotlin-parcelize plugin 활용하기

목표 Parcel의 특징을 이해한다. Parcelable의 특징을 이해한다. Parcel이란? Parcel이란 무엇일까? 먼저 번역을 해보는 것이 이해에 도움이 될 것이다. Parcel은 한국어로 '꾸러미'라는 뜻이다. 짐을 싸듯

kotlinworld.com

 

 

 

 

 


 

[오늘 복습한 내용]

1. itemList.add 코드 정리하기

  • 코드창에 list.add(data("A","B"))로 일일이 추가하면 데이터의 프로퍼티가 한, 두개정도는 괜찮을 수 있어도 리스트 객체양이 많아지거나, 프로퍼티가 많으면 굉장히 코드창이 지저분하고 코드를 축약할 수 없다
  • list.apply { add(data("A","B")) } 처럼 apply로 리스트객체를 추가하고 다 추가한 다음 좌측 열 번호에 있는 - , + 통해 코드 축약 해놓으면 코드창이 깔끔해진다.

 

 


[오류,에러 등등]

1. onBackPressed

  • 뒤로가기 버튼 클릭 시 이벤트를 만들기 위해 구글링 해서 코드를 작성해봤다.
  • 다 작성해도 override 의 빨간색 밑줄이 안없어 지길래 super.onBackPressed() 도 넣어봤으나 해결이 안된다.
  • onBackPressed() 를 ctrl + 클릭해도 라이브러리가 나오지 않길래 봤더니 API 33레벨 이상부터는 onBackPressed() 를 지원하지 않는다고 한다. 대신 OnbackInvokedCallback 또는, OnBackPressedCallback을 사용해야 한다고 한다.
  • 해결방법은 오늘 배운 내용에 작성.

 

 

  • 오류 코드
override fun onBackPressed() {
             var builder = AlertDialog.Builder(this)
            builder.setTitle("앱 종료 확인")
            builder.setMessage("정말로 종료하시겠습니까?")
            builder.setIcon(R.drawable.baseline_android_24)

            val listener = DialogInterface.OnClickListener { p0, p1 ->
                when (p1) {
                    DialogInterface.BUTTON_POSITIVE -> finishAffinity()
                    DialogInterface.BUTTON_NEGATIVE -> { }
                }
            }
            builder.setPositiveButton("종료하기",listener)
            builder.setNegativeButton("취소",listener)
            builder.show()

        }

 

2.  오류까지는 아니지만... putExtra로 어떤 값을 넣어야 하는지 모르겠는데 이런 경우에는 어떤 값을 넣어야 하는지, 특정 값을 넣으면 어떻게 되는지를 알아보며 답을 직접 찾아보고 싶어서 logd 계속 해가면서 결국 찾았다.

 

 

3.

 

 


[느낀 점]

1. 오류가 생기면 logd 로 어디가 문제인지 확인하자

 

2.  어렵다.

 

3. 다양한 코드 천천히 읽어보기.

 

 


[Reference]

// 코드해석 왕
CHAT.OPENAI.COM GPT아저씨

// Parce 등등 정

https://kotlinworld.com/44

// Parcelize 와 Cerializable

https://velog.io/@hygge/Android-Kotlin-RecyclerView-item-%ED%81%B4%EB%A6%AD-%EC%8B%9C-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98

https://jdroid.tistory.com/23

https://altongmon.tistory.com/1022

// putExtra확인

https://kkh0977.tistory.com/687