07
24

 

왼쪽의 이미지에서 날짜를 누르면 우측 화면으로 바뀐다. 다시 달력을 보여주기 위해 뭔가 액션을 취해야했는데, 내가 생각한 방법은 총 3개다.

  1. 바텀시트 behavior를 사용한다(리사이클러뷰에 behavior를 bottom sheet로 다는 방식)
  2. 모션레이아웃을 사용한다
  3. 리사이클러뷰의 스크롤을 활용해서, 최상단일때 뭔가 이벤트를 잡아서 처리한다

1번은 시도했는데, `STATE_EXPAND`, `peek_height` 제어가 까다로워서 해보다가 넘겼고, 모션레이아웃을 사용하는 방법은 최후의 보루로 남겨두기로 했다.

 

그렇게 세번째 방법을 사용했다.

# onScroll로 시도

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)
        // 여기서 스크롤 감지
    }
})

recyclerview에는 콜백 중에 `addOnScrollListener`가 있어서 스크롤을 감지할 수 있다. , 처음 방식은 최상단에 닿았을 때를 기준으로 스크롤이 줄어들면 일정 값 이하일 때 뷰를 다시 만들어주는 형태였다. 최상단을 감지한 방식은 layoutManager의 첫번째 아이템 포지션이 0인 것으로 했다.

layoutManager.findFirstCompletelyVisibleItemPosition()

이걸 저 스크롤안에서 계속 실행되게 하면 최상단 감지가 가능하다.

 

이 방식은 문제가 있다. 스크롤하면서 애매하게 기준 값에 걸쳐서 위아래로 변하면 뷰가 막 사라졌다가 생겼다가 요동친다. 그래서 이 방법은 못써먹겠다고 생각했다.

 

그래서 pullRefresh 방식을 떠올렸고 그러다 찾은게 `edgeEffectFactory`다.

# `edgeEffectFactory`

pullRefresh같은 스크롤 땡김을 오버스크롤이라고 부르는 듯하다.

recyclerView.edgeEffectFactory = object : RecyclerView.EdgeEffectFactory() {
    override fun createEdgeEffect(recyclerView: RecyclerView, direction: Int): EdgeEffect {
        return object : EdgeEffect(recyclerView.context) {
            override fun onPull(deltaDistance: Float) {
                super.onPull(deltaDistance)
                
            }

            override fun onPull(deltaDistance: Float, displacement: Float) {
                super.onPull(deltaDistance, displacement)
                
            }
        }
    }
}

`createEdgeEffect`는 기본적으로 네 방향 모두 지원한다. onPull이 두개인데, API 21 기준으로 버전 분기처리를 해준 코드다.(아래가 더 최신) 

deltaDistance는 오버스크롤한 거리를 나타낸다. displacement는 오버스크롤의 누적된 거리를 나타낸다. 이 두 차이는 극명하다. 오버스크롤을 해보면 순간 나타나고 사라지는데, displacement는 touch가 떨어지지않는한 누적거리를 갖고있기 때문에 좀 더 세밀한 제어가 가능하다는 차이점이 있다.

 

내가 onPull에서 처리한 로직은 아래와 같다.

private fun handlePull(direction: Int, deltaDistance: Float) {
    when (direction) {
        1 -> {
            handlePullState++
        }
    }
    if (handlePullState > 5) {
        updateCalendar(Calendar.RESTORE)
        handlePullState = 0
    }
}

direction이 1이면 상단에서 일어나는 pull 이벤트다. 한 번 호출될 때 handlePullState라는 변수값을 증가시켜서 이게 5번 호출되면 달력 뷰를 복구해주도록 설계했다.

 

 

도움이 됐다면 댓글이나 공감 버튼 한 번씩 누르고 가주세요!

 

반응형
COMMENT