[TIL] Android 앱 개발 숙련 : 개인 과제 3

지혜·2024년 1월 10일

Android_TIL

목록 보기
35/70

✏240110 수요일 TIL(Today I learned) 오늘 배운 것

코드 전문은 깃허브 참조! : Android_ch3_MyAppleMarket

📖선택 구현 목표 만들기 트러블 슈팅

  • 🚩스크롤 상단 이동!

    • 플로팅버튼. UI를 잡는데.. 시간이 너무 오래걸렸다. 맨위로 올라가는 버튼을 만드는 것 자체는 어렵지 않았는데, 이 버튼에 최상단이 아닐 떄와 최상단일 때를 나눠서fade in/out 애니메이션도 줘야하고, 누르면 색도 변해야하고 이게 너무 시간을 오래 잡아먹었다.

      [🚩플로팅 버튼은 스크롤을 아래로 내릴 때 나타나며, 스크롤이 최상단일때 사라집니다.]
      [🚩플로팅 버튼은 나타나고 사라질때 fade 효과가 있습니다.]

      => 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)을 한 번 더 걸어줬다.

      [🚩플로팅 버튼을 클릭하면(pressed) 아이콘 색이 변경됩니다.]

      => 누르면 색을 변하게 하는 것은. 자기소개 어플 만들 때 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로 이미지 크기 조절이 가능하다. 이전에도 이미지 크기 맞춘다고 고생했었는데, 이 방법이 바로바로 적용되서 쉬운것같다. 까먹지않도록 적어놓는다.!@

  • 🚩상품 삭제하기!

    • 롱클릭이라고 해도, 이미 그냥 onClick을 만들어둔게 있으니까, 기본 틀은 만들어둔 onClick을 많이 따랐다. 다이얼로그도 이미 만들어뒀으니까 그걸 활용했다. 다만 어댑터에서 onBindViewHolder에 holder를 걸어줄 떄, setOnLongClickListener를 사용해야 했는데, 얘는 반환값을 꼭 줘야한다고 해서, 당황스러웠지만 안드로이드 스튜디오가 시키는대로 하니까 잘 되어서 다행이다.
      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의 범위를 다시 잡아주는 역할을 한다.

  • 🚩[찐도전과제] 좋아요 처리!!

    • [🚩메인 화면으로 돌아오면 해당 상품에 좋아요 표시 및 좋아요 카운트 +1]
      [🚩상세 화면에서 좋아요 해제시 이전 상태로 되돌림]

      => 상세페이지에서 좋아요 아이콘선택시 아이콘 변경하는 것은 딱히 어렵지 않았다. 문제는 상세페이지에서 백날천날 아이콘을 눌러봤자 그 값이 저장이 안된다는거다. 메인페이지로 돌아오면 +1개수는 추가되는데 다시 상세페이지로 돌아가면 텅 빈하트가 나를 마주하고 있다. 그냥 이모티콘만 텅 빈 하트인게 아니라 누르면 또 +1을 추가해준다..ㅎㅎ 그리고 다시 돌아가보면 텅 빈 하트의 반복... 알고보니까
      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( )를 걸어줘야 완벽하게 적용이 된다는 것을.....ㅎㅎ 안다면, 어떻게든 만들 수 있다.

✏오늘의 느낀점과 내일 할 일

  • 솔직히 하루안에 선택과제를 다한게 믿기지 않는다. 플로팅 만들 때 오전 다잡아먹고 걱정했는데.. 생각보다 상품삭제하기가 쉬웠어서 다행이다. 플로팅은. 진짜 디자인 잡는것 때문에 짜증나서 플로팅버튼 어떤 때에 왜쓰는지 까지 검색해봤다가 (그냥 버튼쓰고싶어서..ㅎ) 구현 목록을 꼭 지키도록 강조하셨던게 생각나서 이악물고 다시 플로팅 버튼을 썼다. 맨처음 클론UI할 때도 플로팅 버튼 때문에 진짜 화가 겁나 났었는데. ㅎ .ㅎ .ㅎ.ㅎㅎㅎ 오늘도 느꼈다. 그래도 두번했으니까 세번째.. 가 오겠지. 세번째는 좀 더 쉬울꺼라고 느낀다. 그리고 그땐 그냥 왠만하면 되는 방향으로 해도.. 되지않을까.?ㅎ/ 좋아요 기능 구현은 튜터님과 매니저님이 겁을 엄청 주셨는데.. 주신 만큼 복잡했지만 하나씩 하다 보면 이해하기에 어렵지 않은 것 같다. 이전 과제들에서 정리를 잘해놨어서 이번 과제에서 사용할 때 어떻게 해야할 지 감이 왔다. 근데, 배우지 않은 부분에 대해서는 솔직히 진짜 무지해서 검색해서 발견한 블로그들이 아니었으면 아마 못하지 않았을까......... 근데 못찾았어도 보완과제 보고 하면 되니까. 사실 완성은 했지만 내일 보완영상을 보면 보완해야할것들이 꽤 있지 않으려나..? 싶다. 어쨌든 마무리 했다는데 의의를 둔다.

  • 내일은 오전 중에 작성한 과제물이 정상작동하는지, 원하는 조건을 충족했는지 마지막으로 확인한 후에 깃허브에 올려서 제출할 것이다. 깃허브에 올리면 이 포스팅에도 연결해둬야한다.
  • 오후에는 이번에 만든 것을 바탕으로 수준별과제 리팩토링과, 보완영상을 보고 개인과제 보완수정도 같이 진행해야겠다.

✏참고한 블로그

profile
파이팅!

0개의 댓글