본문 바로가기

dev/android

Tinder 같은 스와이프 가능한 RecyclerView 만들기 (feat.LayoutManager)

1.LayoutManager?

A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user. By changing the LayoutManager a RecyclerView can be used to implement a standard vertically scrolling list, a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock layout managers are provided for general use.

Android Developer 사이트에서 확인할 수 있는 LayoutManager의 설명입니다.
간단히 설명을 드리면 RecyclerView에 그려지는 View의 크기 및 위치를 정하고, View 재활용을 담당하고 있습니다.
기본적으로 제공되고 있는 LayoutManager는 LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager가 있습니다.

2.LayoutManager Method

많은 함수들이 존재합니다. 이중에 오늘 CardStack View를 만들때 필요한 함수들에 대해서만 알아보겠습니다.

generateDefaultLayoutParams()

RecyclerView에 기본 LayoutPararm을 생성하는 작업을 합니다.

구현 코드

override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
    return RecyclerView.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    )
}

onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State)

이 함수에서 실제 배치될 View를 그려주는 작업을 하게 됩니다.

먼저 이 함수에서는 파라미터 두 개를 받고 있는 것을 알 수 있습니다.
recycler, state입니다.

recycler

RecyclerView에서 아주 중요한 역할을 하는 친구라는걸 이름에서 알 수 있습니다.

A Recycler is responsible for managing scrapped or detached item views for reuse.

scrapped 또는 detached된 View를 재사용하기 위해서 그 View들을 관리해주는 역할을 해준다고 합니다.

  • scrapped : View가 RecyclerView에 붙어있지만 재사용 혹은 제거를 위해 마킹을 한 상태
  • detached : View가 RecyclerView에서 detached된 상태

state

말 그대로 RecyclerView의 상태값입니다.
그리고 내부 컴포넌트끼리 데이터 통신을 위해서 사용되기도 한다고 합니다.

구현 코드

override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
    detachAndScrapAttachedViews(recycler) // 1
    if (itemCount < 1) return

    val startPosition = (itemCount - 1).coerceAtMost(topPosition + preloadCount - 1)

    for (position in startPosition downTo topPosition) {
        val isTopView = position == topPosition
        val drawPosition = position - topPosition
        val view = recycler.getViewForPosition(position)

        changeDefaultElevation(ViewCompat.getElevation(view))

        if (isTopView) setTopViewOnTouchListener(view)

        setElevation(
            view,
            if (isTopView) defaultElevation
            else defaultElevation - 1f * drawPosition
        )

        measureChildWithMargins(view, 0, 0)

        val params = view.layoutParams as RecyclerView.LayoutParams

        layoutDecorated(
            view,
            params.leftMargin + paddingLeft,
            params.topMargin + paddingTop,
            getDecoratedMeasuredWidth(view) + params.leftMargin + paddingLeft,
            getDecoratedMeasuredHeight(view) + params.topMargin + paddingTop
        ) // 2

        if (drawPosition > 0) {
            view.scaleX = validateScale(1 - scaleGap * drawPosition)
            view.scaleY = validateScale(1 - scaleGap * drawPosition)
        } else {
            view.scaleX = validateScale(1f)
            view.scaleY = validateScale(1f)
        }

        if (startXPosition == null || startYPosition == null) {
            initStartPosition(view.x, view.y)
        }

        addView(view) // 3
    }
}

1 : 현재 붙어있는 View들을 scrapped 상태로 변경해줍니다.
2 : view의 left, top, right, bottom 값을 정해줍니다.
3 : View를 실제로 붙여줍니다.

3.결과

 


LayoutManager를 이용하면 RecylcerView에서 다양하게 View를 표현 할 수 있습니다.
다음 포스트에선 이번 포스트에서 스리슬쩍 넘어간 Recycler에 대해서 더 알아보도록하겠습니다.
Github에서 전체 코드를 확인해보세요!

 

'dev > android' 카테고리의 다른 글

Dagger2 (feat. Android Developer, Codelab)  (0) 2020.01.04
Playing with String and DataBinding in xml  (0) 2019.12.31