Android/StoreInfo

<정리> 사과마켓 앱 구현 2

re트 2024. 1. 5. 19:40
728x90

어쩌다보니 오늘 2개를 올리게 됐다.

저번 구현 1은 필수 구현 과제가 다 올라가서 거기다 붙혀서 올리기는 좀 그렇더라

 

1. 플로팅 액션 버튼을 이용한 스크롤 상단 이동

1) 플로팅 액션 버튼 세팅

플로팅 액션 버튼은 xml 상에서 세팅하는게 제일 어려웠다.

뭐하나 기존에 알던 대로 하면 원하는대로 작동을 안 하더라

그렇게 알게 된 것들을 정리해보겠다.

<Material3 기준>

app:shapeAppearance="@style/FloatingButtonTheme” 

 - 버튼의 모양을 커스텀할 수 있는 속성인듯함

 - 기본 : 약간 모서리 깎은 사각형

 - 스타일에서 cornerSize값을 조절해서 원형으로 만듬

android:backgroundTint="@color/selector_fab_click"

 - 배경 색 지정

 - 기본 : 회색

 - color 아래에 selector를 만들어서 클릭했을 때 색깔이 변하도록 세팅(color만 들어갈 수 있었기 때문에)

android:outlineSpotShadowColor="@color/white” 

 - 그림자 색 지정

 - 기본 : 검은색

app:backgroundTint="@color/grey” 

 - 테두리 색 지정

 - 기본 : 연보라색

app:borderWidth="1dp” 

 - 테두리 두께 지정

 - 기본 : 0dp

app:tint="@color/black” 

 - 아이콘 색 지정

 - 기본 : 보라색

app:maxImageSize="20dp” 

 - 이미지 최대 크기 지정

 - 기본 : 20dp보다는 큼

app:fabCustomSize="45dp" 

 - 버튼 크기 지정

 - 기본 : 45dp보다는 큼

 

2) 스크롤 리스너

리사이클러뷰가 스크롤 될 때, 정확히는 아래로 스크롤이 되거나 최상단에 있을 때를 판별하여 플로팅 액션 버튼을 보여주거나 보여주지 않게 해야했다.

그래서 ScrollListener를 추가했다.

스크롤 상태가 변경되고 처리하는 onScrollStateChanged() 함수를 오버라이딩하고 스크롤 상태가 변경될 때 처리하는 onScrolled() 함수를 오버라이딩했다.

그리고 아래로 스크롤 중인지, 최상단에서 멈춰있는지 상태를 확인해서 플로팅 액션 버튼의 isVisible 속성 값을 변경했다.

// 플로팅 액션 버튼을 눌렀을 때 리사이클러뷰 스크롤이 최상단으로 부드럽게 이동
binding.fabMainScrollUp.setOnClickListener {
    binding.recyclerViewMain.smoothScrollToPosition(0)
}

// 리사이클러뷰 스크롤 리스너
binding.recyclerViewMain.addOnScrollListener(object: RecyclerView.OnScrollListener() {
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        if (newState == RecyclerView.SCROLL_STATE_IDLE && !recyclerView.canScrollVertically(-1)) {
            binding.fabMainScrollUp.isVisible = false
        }
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        if (dy > 0) {
            binding.fabMainScrollUp.isVisible = true
        }
    }
})

 

3) 애니메이션 추가

애니메이션은 코드 상에서 하는 방법과 anim 폴더 아래에 xml 파일을 만들어서 하는 방법이 있었는데 나는 xml 파일을 만들어서 하는 방법을 선택해서 진행했다.

애니메이션으로 선택해서 xml 파일을 만드니까 가장 상위 태그가 set이더라

그 아래에 투명도를 조절하는 alpha 태그를 넣어서 코드를 완성했다.

페이드인과 페이드아웃은 시작 투명도와 끝 투명도를 반대로만 하면 되더라

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="500"/>
</set>

 

코드 상으로 불러오는 건 AnimationUtils.loadAnimation을 사용했다.

여기서 알게 된 것은 전역 변수로 하려고 처음에

private val fadeIn = AnimationUtils.loadAnimation(this@MainActivity, R.anim.fade_in)

이렇게 코드를 짜고 돌렸는데 null 포인터 에러가 발생하며 튕겼다.

그래서 이걸 어쩌지 하다가 context 때문인 거 같은 느낌이 들어서

private val fadeIn: Animation by lazy {
    AnimationUtils.loadAnimation(this@MainActivity, R.anim.fade_in)
}

이렇게 바꿨더니 해결되었다.

 

실제로 애니메이션을 실행시킬 때는 이 애니메이션 변수를 해당 뷰에 startAnimation 안에 넣어주면 된다.

binding.fabMainScrollUp.startAnimation(fadeIn)

 

2. 상품 삭제

1) 아이템 롱클릭 처리

ItemAdapter에서 처리했다.

이전에 만든 ItemClick 인터페이스처럼 ItemLongClick 인터페이스를 만들었다.

그리고 이외의 코드는 ItemClick과 동일하게 작성했다.

이번 과제를 하면서 어댑터로 뷰바인딩 객체를 받아온다는게 참 편하다는 걸 느낀다.

 

2) 다이얼로그 생성

다이얼로그도 종료 확인 때 썼던 다이얼로그와 거의 비슷하다.

다른 건 리스너에 들어가는 DialogInterface.OnClickListener의 실행 코드 뿐이다.

물론 롱클릭 함수에 들어있다는 것도 다르다.

val builder = AlertDialog.Builder(this@MainActivity)
builder.setTitle("상품 삭제")
builder.setIcon(R.drawable.img_recycler_message)
builder.setMessage("상품을 정말로 삭제하시겠습니까?")

val listener = DialogInterface.OnClickListener { _, p0 ->
    if (p0 == DialogInterface.BUTTON_POSITIVE) {
        ...
    }
}

// 작업 버튼 설정
builder.setPositiveButton("확인", listener)
builder.setNegativeButton("취소", null)

builder.show()

 

3) 롱클릭된 아이템 삭제 & 업데이트

이건 쉽게 됐다.

position 값을 알고 있었기에 datalist에서 해당 position의 값을 제거하고 datalist가 변경되었음을 어댑터에게 알려줬다.

dataList.removeAt(position)
adapter.notifyDataSetChanged()

 

이제 남은 건 좋아요 처리뿐...

다음주 월요일에 마무리가 되면 좀더 최적화에 신경을 써봐야겠다.

반응형