Android RecyclerView ItemDecoration in Kotlin

2023. 7. 21. 20:00·Android
728x90
반응형

내가 이 클래스를 사용하게 된 이유

내가 사용하던 RecyclerView에는 각 아이템 하단에 구분선(Divider)이 있다.

이전에는 해당 아이템을 구성하는 xml에 MaterialDivider를 배치했었다.

새로운 요구사항이 들어오면서 구조 변경의 필요성을 느끼게 되었다.

새로운 요구사항은 마지막 아이템은 구분선이 가로 전체를 차지하도록 변경하는 것이었다.

 

새로운 요구사항에 따른 시도

RecyclerView Adapter의 onBindViewHolder에서 마지막 아이템을 확인해서 AttributeSet을 다시 할당해주었다.

 

나름 그럴싸한 로직이라 생각했다.(내 기준)


그 결과, 알 수 없는 버그들이 밀려들어왔다.


처음 adapter를 할당했을 땐 마지막 아이템에만 해당 AttributSet이 적용되었다.


하지만, 특정 아이템이 변경되거나 모든 아이템이 변경되어 그 결과를 adapter에게 notify할 때 마다
AttributSet이 적용된 바로 전 아이템에 AttributSet이 추가로 적용되었다.

 

그렇게 리스트가 10개이면 notify가 10번 되는 순간 모든 아이템에 AttributSet이 적용된다.

(아직도 이해 불가능한 버그였다.)

 

엄청 검색을 많이 했는데 onBindViewHolder에서 할 수 있는 명확한 해결법은 없었다.

(recyclerview last item index, recyclerview 레이아웃 변경, recyclerview 마지막 아이템 레이아웃 등

수많은 검색어 조합으로 찾아봤었다.)

 

사용 방법

먼저 RecyclerView.ItemDecoration을 상속받은 클래스를 하나 만든다.

 

onDrawOver 메소드를 오버라이딩한다.

 

위 메소드에서 divider 역할을 해주기 위해 Paint를 사용해 네모를 그린다.

 

왜 네모를 그리냐? 라고 나도 누군가의 코드를 보고 이상하다 생각했다.

 

하지만 네모의 양쪽 높이의 좌표가 딱 1픽셀 차이가 나면 엄청 얇은 네모가 된다.

 

사용자의 입장에서 이것은 선으로 보인다.

(이 아래부터는 선을 그린다고 하겠다.)

 

전체 코드

class RecyclerItemDecoration : ItemDecoration() {
    private val paint = Paint()

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDrawOver(canvas, parent, state)

        val widthMargin = 64F
        val height = 1

        for (i in 0 until parent.childCount) {
            val child = parent.getChildAt(i)
            val params = child.layoutParams as RecyclerView.LayoutParams
            val left = parent.marginLeft.toFloat()
            val right = parent.width - parent.marginRight.toFloat()
            val top = child.bottom.toFloat() + params.bottomMargin
            val bottom = top + height

            canvas.drawRect(left + widthMargin, top, right - widthMargin, bottom, paint)
        }
    }
}

이 코드의 경우, 변수명에서 유추할 수 있겠지만 좌우에 대한 margin값을 주기 위해 선을 그릴 때 해당 마진을 추가해준 것이다.

 

그리고 모든 아이템에 대해 동일한 선을 그린다.

 

만약, 내 경우처럼 마지막 아이템만 특별한 처리가 필요한 경우는 drawRect 이전에 if문을 삽입해주면 될 것이다.

if (i == parent.childCount - 1) {
	widthMargin = 0F
	c.drawRect(left + widthMargin, top, right - widthMargin, bottom, paint)
	break
}

이런 식으로 말이다.

 

위 과정을 거쳐 만든 ItemDecoration은 자동으로 적용되지 않는다.

 

당연히 어딘가에 set을 해줘야한다.

 

어디에 해야할까?

 

적용될 RecyclerView에 해주면 된다.

binding.recycler?.addItemDecoration(RecyclerItemDecoration())

그럼 나처럼 고생하는 사람이 없길 바라며 글을 마친다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Android' 카테고리의 다른 글

Android BottomSheetDialogFragment Height 조절하기  (0) 2023.08.28
Android DialogFragment Button Text Size(텍스트 크기 조절)  (0) 2023.08.27
Codelab으로 Room 알아보기 - 2. Create the SleepNight entity(ㄴ)  (0) 2023.07.09
Codelab으로 Room 알아보기 - 2. Create the SleepNight entity(ㄱ)  (0) 2023.07.08
Android Notification Max Count  (0) 2023.07.04
'Android' 카테고리의 다른 글
  • Android BottomSheetDialogFragment Height 조절하기
  • Android DialogFragment Button Text Size(텍스트 크기 조절)
  • Codelab으로 Room 알아보기 - 2. Create the SleepNight entity(ㄴ)
  • Codelab으로 Room 알아보기 - 2. Create the SleepNight entity(ㄱ)
BonusTrack02.dev
BonusTrack02.dev
공부, 일상
  • BonusTrack02.dev
    BonusTrack02.dev
    BonusTrack02.dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (237)
      • Android (84)
      • Language (63)
        • Java (19)
        • Kotlin (27)
        • Swift (17)
      • 프로그래머스 (68)
      • 주저리주저리 (22)
        • 카페 (5)
        • 음식점 (4)
        • 컨퍼런스 (1)
        • 팝업스토어 (4)
        • 해외여행 (0)
        • 전시회 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ViewModel
    안드로이드
    코틀린
    programmers
    viewModelScope
    배열
    자바
    프로그래머스
    Kotlin
    ios
    coroutines
    aac
    오블완
    databinding
    Java
    코루틴
    스위프트
    MVVM
    티스토리챌린지
    Observer
    daterangepicker
    room
    PCCE
    android
    CodeLab
    LiveData
    getNumericValue
    SWIFT
    Material
    jetpack
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
BonusTrack02.dev
Android RecyclerView ItemDecoration in Kotlin
상단으로

티스토리툴바