[Android] setHasFixedSize(true) 의 정체

H43RO·2021년 10월 16일
7

Android 와 친해지기

목록 보기
21/26
post-thumbnail

다시 꺼내보는 RecyclerView 이야기

필자는 예전 포스팅에서 ViewHolder 패턴에 대해서 알아보고, ListView 에서 ViewHolder 패턴을 강제하는 형태로 등장한 것이 RecyclerView 라고 이야기한 적이 있다.

일단 RecyclerView 의 사용 이유에 대해선 알게 됐다. 알게 됐는데, 한 가지 정곡을 찌르는 부분이 있다.

이 글을 보고있는 당신!

RecyclerView 를 사용할 때 무의식적으로 복붙하여 사용한 아래 코드, 당신은 이 코드의 의미를 알고 있나?

setHasFixedSize(true)

XML 상으로 존재하는 리사이클러뷰에 레이아웃 매니저를 설정해주고, 정의해둔 어댑터를 설정해줄 때 위 코드를 같이 기재하게 된다. 그런데 이 코드를 무지성으로 타이핑하고, 무슨 역할을 하는지도 알려고 시도하지 않았다면 반성해야 한다. (필자 역시 마찬가지다)

거 그냥 리사이클러뷰가 뭐.. 고정된 사이즈를 가졌단 뜻 아니오!?

그러니까 고정되고 나발이고 이를 굳이 코드를 통해 명시해주는 이유가 있을 것 아닌가. 이에 대해서 알아보도록 하자.


왜 필요한데?

아래의 코드를 잠시 살펴보자. 리사이클러뷰에 새로운 아이템이 삽입되거나, 기존의 아이템이 제거될 때 호출되는 메소드를 매우 함축하여 간결히 표현한 코드이다.

void onItemsInsertedOrRemoved() {
   if (hasFixedSize) layoutChildren();
   else requestLayout();
}

코드를 보면 hasFixedSizetrue 인 경우 layoutChildren() 을 호출하고, 만약 false 라면 requestLayout() 을 호출하게 된다.

🤷🏻‍♂️ 어? requestLayout() 얘 어디서 많이 봤는데??

그렇다. 우리는 안드로이드에서 View 의 라이프사이클에 대해 다뤄본 적이 있다. (해당 포스팅 참고)
이 라이프사이클에 의거하면, View 의 색깔 및 모양이 아닌 크기가 변화할 때 requestLayout() 을 호출하여 다시 ViewGroup 에 속한 뷰들의 크기 측정부터, 자신의 크기 측정까지 일일히 수행한다고 했다.

🤦🏻‍♂️ 그렇다면 상당히 큰 비용이 드는 작업이겠죠??

당연하다. 부모 노드부터 자식 노드 끝까지 모든 뷰들의 크기를 다시 측정하고, 다시 각각의 위치를 지정하고 그리는 작업을 수행해야 하기 때문에 매우 고비용 작업에 속한다.

😃 아하, 그럼 hasFixedSizefalse 일 때 requestLayout() 을 호출한다는 말은 결국..!

리사이클러뷰의 아이템이 들어오거나 나갈 때마다 RecyclerView 전체의 레이아웃을 다시 잡는다는 의미이다.


와 그렇게 비효율적으로 동작한다고?

생각해보면 이것이 당연한 수순이다. 아이템이 들어올 때, 리사이클러뷰의 크기가 어떻게 바뀔지 모르니까 레이아웃 전체가 바뀌어야하기 때문에 무조건 필요한 절차이다.

그런데 사실 리사이클러뷰를 사용하는 대부분의 경우 정해진 크기를 갖고 있다. 그러니까, height 혹은 width 의 값을 고정하거나 match_parent 속성을 준 경우를 말한다. 만약 RecyclerView 가 항상 같은 크기를 유지한다면, RecyclerView 전체의 레이아웃을 다시 잡을 필요없이 자식들, 즉 아이템들에 대해서만 레이아웃을 잡아주면되는 것 아닌가?

따라서 setHasFixedSize(true) 를 해주게 되면, 위 분기문에서 layoutChildren() 으로 빠져 더욱 효율적으로 뷰 그리기 작업을 수행할 수 있는 것이다. 리사이클러뷰의 크기가 항상 고정된, 즉 변하지 않는다는 것을 개발자가 안드로이드에게 알려줌으로써 고비용 작업을 줄이는 것이다.

setHasFixedSize(true)를 전달받은 안드로이드는 이렇게 동작할 것이다.

아하! 리사이클러뷰의 크기가 변할 일이 없구나! 그럼 누구 새로 들어오거나 나갈때 아이템들의 자리만 다시 잡아주면 되겠구낭



결론

우리는 setHasFixed(true) 를 해줌으로써 대부분의 경우에 High-cost 작업을 피할 수 있다. 어떻게 보면 저 코드 한 줄로 큰 최적화를 이뤄냈다고도 할 수 있겠다. 물론, 리사이클러뷰의 크기가 동적으로 변화하는 경우 (height 혹은 widthwrap_content 로 지정한 경우) 가 있을 경우 false 로 지정해야 한다. 만일 동적인 리사이클러뷰에 setHasFixed(false) 를 지정하지 않는다면 제대로 동작하지 않을 것이다.

참고자료

https://stackoverflow.com/questions/28709220/understanding-recyclerview-sethasfixedsize

profile
어려울수록 기본에 미치고 열광하라

1개의 댓글

comment-user-thumbnail
2023년 10월 20일

좋은 글 감사합니다~

저 궁금한게 하나 있는데 드롭다운 버튼을 눌러서 안보이게 설정했던 부분을 보이게하고, 다시 버튼을 눌러서 다시 안보이게 설정하는 Expandable RecyclerView의 경우에서는 setHasFixedSize(true) 옵션을 지정하지 않는게 맞을까요?

새롭게 보여질 뷰가 크기가 고정되어 있긴 할텐데(default 로 하단 뷰는 visible false 이기 때문), 드롭다운 버튼을 통해서 뷰의 크기를 변화시킬 수가 있어가지구요

답글 달기