내가 이 클래스를 사용하게 된 이유
내가 사용하던 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())
그럼 나처럼 고생하는 사람이 없길 바라며 글을 마친다.
'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 |