본문 바로가기

내일배움캠프

본 캠프 38일 차

해설 코드보고 개인과제 수정 및 추가하기

디테일페이지

우선 이미지를 받아오는 코드

        //데이터 받아오기
        var data = intent?.getParcelableExtra<MyItem>(Contants.Item_OBJECT)?:
        //?: 로 null일 경우 앱이 꺼지지 않게 넣는 데이터
        MyItem(R.drawable.sample1,"제목","위치","내용","아이디",1000,1,1,false)
        //받은 데이터 집어넣기
        binding.imgTitle.setImageResource(data.aImg)
        binding.userId.text = data.aUserid
        binding.userPrice.text = DecimalFormat("#,###").format(data.aPrice) + "원"
        binding.userLocation.text = data.aUserlocation
        binding.userSubtitle.text = data.aSubtitle
        binding.userTitle.text = data.aTitle

이렇게 했었는데 튜터님께 물어보니 엘비스 연산자나, !! 같은 건 안좋다고 한다. 

그래서 내가 했던 거에서 고치자면

        //데이터 받아오기
        var data = intent?.getParcelableExtra<MyItem>(Contants.Item_OBJECT)
        //받은 데이터 집어넣기
        binding.imgTitle.setImageResource(data?.aImg as Int)
        binding.userId.text = data?.aUserid
        binding.userPrice.text = DecimalFormat("#,###").format(data?.aPrice) + "원"
        binding.userLocation.text = data?.aUserlocation
        binding.userSubtitle.text = data?.aSubtitle
        binding.userTitle.text = data?.aTitle

이렇게 as Int로 Int라고 정해주면 된다고 한다.그리고 다른 방법으로는 해설 코드에 나와있는 대로 

setImageDrawable(ResourcesCompat.getDrawable(ResourcesCompat.getDrawable(resources,resId,null))

이 방식을 그냥 쓰라고 한다. 대신 저기 resId는 지금 받아온 데이터를 넣어야 하니

binding.imgTitle.setImageDrawable(ResourcesCompat.getDrawable(resources,data?.aImg,null))

이렇게 바꾸면 되는데 다만 이러면 어제처럼 Int? 로 뜨면서 이 코드를 쓸 수가 없다. 여기에 아까 배운 as Int로 인트라고 해주면

binding.imgTitle.setImageDrawable(ResourcesCompat.getDrawable(resources,data?.aImg as Int,null))

올바르게 오류가 뜨지 않는다. 다만 여기서 as Int 대신 let으로 바꾸면(오류뜨는거 alt+shift+enter 누르면 바뀐다)

binding.imgTitle.setImageDrawable(data?.aImg?.let {
    ResourcesCompat.getDrawable(resources,
        it,null)
})

이렇게 해설 코드와 똑같이 바뀐다.

 

길게 클릭해서 지우기

//길게 클릭해서 지우기
adapter.itemLongClick = object : MyAdapter.ItemLongClick {
    override fun onLongClick(view: View, position: Int) {
        val del = AlertDialog.Builder(this@MainActivity)
        del.setIcon(R.mipmap.ic_launcher)
        del.setTitle("상품 삭제")
        del.setMessage("상품을 정말 삭제하시겠습니까?")

        //확인을 눌렀을 때 뒤로가게끔
        del.setPositiveButton("확인",
            DialogInterface.OnClickListener { dialog, _ ->
                dataList.removeAt(position)
                adapter.notifyItemRemoved(position)
        del.setNegativeButton("취소",
            DialogInterface.OnClickListener { dialog, _ ->
                //null 대신 dismiss()넣기
                dialog.dismiss()
            })
        del.show()
    }
}

이건 해설 코드 그대로 가져와봤는데, onBackPressed로 뒤로가기 다이얼로그 만들 때랑 비슷했다. 다만 리스트에서 삭제하는 기능이 들어가야 하는데 저렇게 리스트.removeAt(position)을 해주면 삭제가 되긴하는데 삭제되고 그 빠진 리스트로 다시 보여져야 하니 notifyItemRemoved(position)을 꼭 사용해야한다.

근데 이상태로 돌려보니 위 코드는 0번이라는 번호를 삭제하고, 리스트에는 1번부터 나타난다. 그런데 정작 그 아이템을 누르면 만약 3번을 눌렀다면 4번의 아이템이 나온다. 그냥 0번이란거를 리스트에서만 삭제하고 데이터를 넘길 때는 0번이 살아나는거 같다. 그래서 찾아보니

adapter.notifyItemRangeChanged(position,dataList.size)

 이 코드를 써야했다. 다시 바뀐 리스트를 보여주는 코드인 것 같다.

 

토스트메세지 말고 스낵바 + 좋아요

fun showSnackbar(){
     Snackbar.make(binding.root,"관심목록에 추가되었습니다",Snackbar.LENGTH_SHORT).show()
}

해설이랑 조금 다르게 써볼겸 해서 함수로 써놨는데 

isLike = data?.isLike == true

//좋아요 눌렀을 때와 안눌렀을 때 사진 두개 다 넣기
binding.ivHeart.setImageResource(if(isLike){R.drawable.heart2}else{R.drawable.heart})

//좋아요 눌렀을 때
binding.layHeartClick.setOnClickListener {
    //좋아요를 처음 눌렀을 때
    if (!isLike) {
        binding.ivHeart.setImageResource(R.drawable.heart2)
        showSnackbar()
        isLike = true
        //그 외
    }else {
        binding.ivHeart.setImageResource(R.drawable.heart)
        isLike = false
    }
}

showSnackbar()대신 그 안에 있는 코드를 집어넣어도 된다.

그리고 좋아요 이미지를 visible과 invisible로 사용하는 줄 알았는데 저렇게 setImageResource안에 if로 해서 하면 두가지를 넣을 수 있었다. 좋아요가 눌렸다, 안눌렸다를 판별하기 위해 MyItem에도 val이 아닌 var로 string이 아닌 boolean으로 선언해준다. 이제 그게 들어갔으니 메인에도 일단 초기에 안눌린 상태이니 false를 모든 아이템 데이터에 추가 해 준다.

좋아요 쓰기 위해서는 메인->디테일->메인 으로 데이터가 다시 메인으로 돌아가야하므로 ActivityResultLauncher를 사용해서 intent해주는거로 바뀌었다.

lateinit var activityResultLauncher: ActivityResultLauncher<Intent>

맨 위에 이렇게 선언을 먼저 해주고 onCreate안에 

    //디테일에서 받을 때
    activityResultLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == RESULT_OK) {
                val itemNum = it.data?.getIntExtra(Contants.Item_NUMBER, 0) as Int
                val isLike = it.data?.getBooleanExtra("isLike", false) as Boolean

                if (isLike) {
                    //안되었던 이유 -> MyItem에 선언된게 val이라 변할 수 없어서 var로 고쳐줘야함
                    dataList[itemNum].isLike = true
                    dataList[itemNum].aHeart += 1
                } else {
                    if (dataList[itemNum].isLike) {
                        dataList[itemNum].isLike = false
                        dataList[itemNum].aHeart -= 1
                    }
                }
                //어댑터 갱신해주는 코드
                adapter.notifyItemChanged(itemNum)
            }
        }
}

이렇게 써서 좋아요가 눌렸을 경우와, 좋아요가 눌리지 않았을 경우 각각 +1, -1 해주는 코드를 넣어준다. 근데 

	else {
		dataList[itemNum].isLike = false
		dataList[itemNum].aHeart -= 1                  
	}

이렇게만 써버리면 누르지않고 그냥 뒤로가기를 눌렀을 때도 계속 -1씩 되기에 위에 

		else {
                    if (dataList[itemNum].isLike) {
                        dataList[itemNum].isLike = false
                        dataList[itemNum].aHeart -= 1
                    }
                }

이렇게 if문으로 저렇게 감싸주면 내가 하트를 뺐을 때만 깎인다.

그리고 제일 중요한건 마지막 어댑터 갱신해주는 코드를 꼭 작성해야 적용이 된다.

 

플로팅버튼

스크롤 최상단으로 올리는 플로팅버튼 사용하는 건 뭔가 많이 복잡해 보였다.

//플로팅 버튼
binding.recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener(){
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        fadeScroll()
    }
})

일단 그대로 따라썼는데

fun fadeScroll() {
    //애니메이션 효과
    val fadeIn = AlphaAnimation(0f,1f).apply { duration = 500 }
    val fadeOut = AlphaAnimation(1f,0f).apply { duration = 500 }
    var isTop = true

    binding.recyclerview.addOnScrollListener(object : RecyclerView.OnScrollListener(){
        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
            //맨 위 일때
            if (!binding.recyclerview.canScrollVertically(-1)
                && newState == RecyclerView.SCROLL_STATE_IDLE) {
                binding.fbTotop.startAnimation(fadeOut)
                binding.fbTotop.visibility = View.GONE
                isTop = true
                //아래일 때
            }else {
                if (isTop) {
                    binding.fbTotop.visibility = View.VISIBLE
                    binding.fbTotop.startAnimation(fadeIn)
                    isTop = false
                }
            }
        }

    })

}

각 애니메이션 효과 (0f는 0%, 1f는 100%를 나타냄)를 dration을 줘서 천천히 사라지고 나타나게 하고... 밑에는 그냥 공식처럼 외워야 할거 같다.

뭔가 수정하고 이런거에 많이 시간 잡아먹었는데...주말에 베이직반 과제도 해봐야겠다.

'내일배움캠프' 카테고리의 다른 글

본 캠프 40일 차  (0) 2024.01.16
본 캠프 39일 차  (0) 2024.01.15
본 캠프 37일 차  (0) 2024.01.11
본 캠프 36일 차  (1) 2024.01.10
본 캠프 35일 차  (0) 2024.01.09