코드 전문은 깃허브 참조! : Android_ch3_MyAppleMarket
플로팅버튼. UI를 잡는데.. 시간이 너무 오래걸렸다. 맨위로 올라가는 버튼을 만드는 것 자체는 어렵지 않았는데, 이 버튼에 최상단이 아닐 떄와 최상단일 때를 나눠서fade in/out 애니메이션도 줘야하고, 누르면 색도 변해야하고 이게 너무 시간을 오래 잡아먹었다.
=> fade in/out 애니메이션은 인터넷에 만들어두신 분들께 많아서.. 그걸 참고..ㅎㅎ하고 연결하는 법도 겸사겸사 배워왔다. 파프리카마켓 때에는 액티비티 전환 애니메이션이여서 ActivityOptionsCompat클래스를 사용해서 Bundle( )로 전달했지만, 여기서는 버튼이 등장하고 안하고의 애니메이션이라서 startAnimation(AnimationUtils.loadAnimation(this@MainActivity, R.anim.fade_in))을 사용했다. startAnimaion 메서드가 따로 있었다. 그래서 애니메이션 자체를 거는것은 어렵지 않았으나, 문제는 플로팅 버튼이 최상단일 떄에는 안나오고, 스크롤되었을 때는 나타나서 유지되어야하는 것이다.
//플로팅버튼 스크롤상태에 따라 fadein/fadeout효과를 통해 표시하기.
//addOnScrollListener를 통해 스크롤 상태 감지
binding.productRecycle.addOnScrollListener(object : RecyclerView.OnScrollListener() {
//ctrl+o를 통해 onScrolled를 상속받아서 사용
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
//스크롤이 되는 중일때 호출한다.
super.onScrolled(recyclerView, dx, dy)
//스크롤이 맨위에 도달 했을 때(최상단) = -1 /최하단 = 1
if (!recyclerView.canScrollVertically(-1)) {
//플로팅 버튼이 보여지고 있을 때에만 사라지도록
if (binding.btnFloating.visibility != View.GONE) {
binding.btnFloating.apply {
visibility = View.GONE
startAnimation(AnimationUtils.loadAnimation(this@MainActivity, R.anim.fade_out))
}
}
} else {
//플로팅 벝튼이 사라졌을 때만 보여지도록
if (binding.btnFloating.visibility != View.VISIBLE) {
binding.btnFloating.apply {
visibility = View.VISIBLE
startAnimation(AnimationUtils.loadAnimation(this@MainActivity, R.anim.fade_in))
}
}
}
}
})
처음에는 스크롤 방향이 아래로 내려질 때만 버튼을 나타나게 해야해서 if (dy > 0) {//스크롤방향이 아래로 향하는 경우} else {//스크롤방향이 위로 향하는 경우}를 썼었는데, 이 경우 최상단이 아니여도 스크롤을 위로 했을 때 버튼이 사라져서.. 스크롤이 맨 위에 도달한 경우를 따로 찾아서 if(!recyclerView.canScrollVertically(-1)) 조건문을 걸어주었더니, 원하는 모양이 나왔다. 근데 그냥 그안에 visibility와 애니메이션을 걸었더니, 스크롤 할 때 마다 버튼의 애니메이션이 다시 로드되어서, 이미 버튼이 보여지고 있을 때에는 반응하지 않도록 if (binding.btnFloating.visibility != View.GONE)을 한 번 더 걸어줬다.
=> 누르면 색을 변하게 하는 것은. 자기소개 어플 만들 때 drawable폴더에 selector xml파일을 따로 만들어서 state_pressed="true" 일때와 false일 때를 구분하도록 하는 방법을 쓰고 싶었는데.. 플로팅버튼은 androidx가 아니고 외부 라이브러리?에서 가져오는 것이기 때문에 android:background가 먹히지 않았다. .. 그래서 중간에 그냥 AppCompatButton을 사용해서 디자인디테일을 똑같이 잡았는데.. 또 굳이 도전과제 조건에 '플.로.팅'버튼을 사용하여~ 라고 적혀있어서 다시 플로팅버튼으로 돌아와서 app:rippleColor="@color/blue" 리플 조건을 걸어주는 걸로 그냥 혼자서 합의를 봤다.ㅎ 나는 기본 테마 자체를 parent="Theme.Material3.DayNight.NoActionBar" 말고 parent="Theme.AppCompat.Light.NoActionBar"를 썼기 때문에 기본색에서 득을 많이 봤는데, 솔직히 중간에 막 만져서 도중에 왜 바뀌었는지는 모르겠지만, app:borderWidth="1dp"을 줘도 색상이 두꺼워지는게 아니고, 버튼이 볼록?해 보이도록 테두리 효과가 생겨나서(밑에 반정도밖에 테두리가 없긴하지만..) 그림자보정이랑 같이 놔두니까 얼추 비슷한 모양이 되었다. 똑같다고는 못하지만 90프로 이상은.. 똑같아 보이지 않을까? 일단 다음 도전과제들 때문에 이정도로 만족하기로 했다.
번외로, 플로팅 버튼에 이미지삽입할 떄 foreground 였나? fore~ 이거 말고 src로 삽입하면 maxImageSize로 이미지 크기 조절이 가능하다. 이전에도 이미지 크기 맞춘다고 고생했었는데, 이 방법이 바로바로 적용되서 쉬운것같다. 까먹지않도록 적어놓는다.!@
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnLongClickListener{
productLongClick?.onLongClick(it,position)
// true를 반환하여 이벤트롤 완전히 처리 (false는 이벤트가 계속 전파된다.)
return@setOnLongClickListener true
}
...
}dataList.removeAt(position)만 걸어놨었다. 음~ 리스트 업데이트? dataList삭제. 쉽네~ 라고 생각했었는데. 이렇게만 해놓으니까 롱클릭으로 삭제했는데 리사이클러뷰 아이템 목록에는 그대로 남아있고, 대신에 리사이클러뷰에서 삭제 안된 아이템을 들어가보면 dataList의 포지션이 이미 바뀌어있어서 다른 상세페이자가 떴다. 아하~ 저 리스트가 데이터리스트가 아니고 리사이클러뷰리스트를 얘기하는거였구나~!!@ 를.. 그때 알아서 다시 AI한테도 물어보고, 구글링도 해서 adapter.notifyItemRemoved(position)와 adapter.notifyItemRangeChanged(position,dataList.size)를 찾아냈다. 내가 dataList에서 지운 사항을 adapter와도 공유해야했던 것이다! 각각 정확히 지운 position을 어댑터에서도 지우고, 지운만큼 범위가 축소되도록 getItemCount의 범위를 다시 잡아주는 역할을 한다.binding.ivDetailLiked.setImageResource(
if(isLiked) { R.drawable.item_heart_filled} else {R.drawable.item_heart}
) 이렇게만 작성했을 때, isLiked는 상세페이지에 선언해둔 private var isLiked = false의 값이 들어가는 것이기 때문에, isLiked = productData?.isLiked == true
binding.ivDetailLiked.setImageResource(
if(isLiked) { R.drawable.item_heart_filled} else {R.drawable.item_heart}
) 이렇게 isLiked에 productData에서 가져온 isLiked가 true인지 확인하여 할당해주는 작업이 꼭 필요했던 것이다. isLiked = productData?.isLiked == true를 통해서 비로소 binding.ivDetailLiked~안에 있는 isLiked에 productData?.isLiked 값이 들어가는 거였다. 이게 이해가 안되서 한참을 고생헀었다..ActivityResultLauncher<Intent>를 통한 쌍방향통신 인텐트를 사용하는 방법으로 잘 연결하면 어떻게든 된다. 아. 그리고 isLiked를 사용하기 위해서 data class에 추가로 var isLiked : Boolean을 추가해줬었다. 이거 찾느라고 구글을 엄청 오래 붙잡고 있었다.. 그리고 또, 만들어준 뒤로가기 아이콘에만 finish( )대신에 만든 exit( )함수를 걸었는데.. 그 핸드폰 자체의 뒤로가기에도 exit( )를 걸어줘야 완벽하게 적용이 된다는 것을.....ㅎㅎ 안다면, 어떻게든 만들 수 있다.