💡 Intro
리사이클러뷰를 사용하다보면 유저가 특정 아이템을 클릭했을 때 처리를 해줘야 할 필요가 있다. 이를 구현하는 방법은 여러가지가 있겠지만 나는 Activity → Adapter → ViewHolder로 람다를 전달하는 방식으로 구현한다. 이렇게 구현하면 어댑터나 뷰홀더가 어느 함수에 강하게 결합되어 있지 않기 때문에 재사용하기 좋아진다.
그런데 아이템을 클릭했을 때 해당 뷰홀더가 가지고 있는 데이터를 사용하여 어떤 작업을 하기 위해서는 약간의 처리가 더 필요하다.
클릭한 뷰홀더에 바인딩 되어있는 String을 사용해야 한다고 가정하자.
그러면 Activity → Adapter → ViewHolder로 전달하는 람다가 다음과 같이 될 것이다.
Activity → Adapter : (String) -> Unit
Adapter → ViewHolder : (Int) -> Unit
여기서 String은 클릭한 뷰홀더에 바인딩 되어있는 값이고, Int는 String을 가져오기 위한 Position이다.
뷰홀더에서는 이를 위해 AdapterPosition과 LayoutPosition을 사용할 수 있다. 그런데 둘 중 어느것을 써도 아무런 문제가 발생하지 않는다. 어떤 차이가 있는지 궁금해서 알아보게 되었다.
1️⃣ AdapterPosition(BindingAdapterPosition)
공식문서를 보면 다음과 같이 설명한다.
그러니까 리사이클러뷰의 데이터가 변경되면 notify*
메서드를 사용해서 데이터의 변경을 어댑터에 알려준다. 그러면 변경된 데이터를 감지하고 뷰를 다시 그리는 과정이 진행된다.
위 과정이 16ms미만이기 때문에 크게 중요하진 않지만 뷰홀더 위치를 사용하여 어댑터에 접근하려는 경우 문제가 될 수 있다. 그려져있는 뷰홀더의 위치를 사용하여 어댑터에서 데이터를 가져오려고 하면 실제와 다른 데이터가 전달될 수 있는 것이다.
public final int getAdapterPosition() {
if (mOwnerRecyclerView == null) {
return NO_POSITION;
}
return mOwnerRecyclerView.getAdapterPositionFor(this);
}
getAdapterPosition은 위에 보이는 것처럼 RecyclerView의 Adapter에 접근해서 해당 뷰홀더가 어디에 위치하는지를 반환한다.
이 때문에 notifyDataSetChanged
를 호출한 경우에는, 다음 레이아웃 패스까지 이 메서드의 반환 값은 NO_POSITION
이 된다고 한다.
2️⃣ LayoutPosition
역시나 공식문서를 먼저 봐보도록 하자.
그러니까 데이터 변경 이후 레이아웃이 다시 그려진 이후의 뷰홀더의 위치를 반환하기 때문에 뷰가 그려지기 전까지 (16ms 이전까지) AdapterPosition과 값이 다를 수 있다.
앞서 이야기한 것처럼 notify*
메서드를 통해 데이터의 변경을 알리면 레이아웃을 요청하고 그리는데까지 16ms 정도가 걸리고 이 시간만큼 AdapterPosition과 값이 다른 것이다.
public final int getLayoutPosition() {
return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
}
정리
AdapterPosition을 사용하면 데이터 변경 이후 16ms 이전에도 정확한 위치에 접근이 가능하고 16ms 이후에도 정확한 위치에 접근이 가능하다.
LayoutPosition을 사용하면 데이터 변경 이후 16ms 이전에는 정확한 위치에 접근할 수 없고 16ms 이후에는 정확한 위치에 접근이 가능하다.
여기서 말하는 16ms는 공식문서에서 제공하는 정량적 수치이고 이 것이 의미하는 바는 뷰의 업데이트가 완료 되었는가이다.
그럼.. LayoutPosition은 언제 사용하지?
RecyclerView 컴포넌트가 어댑터 업데이트를 느리게 처리하는 동안 일관성을 유지하기 위해 주로 사용됩니다.
이 말이 무슨 말인지 아직은 모르겠다.
참고
https://velog.io/@paulus0617/Android-bindingAdapterPosition-vs.-absoluteAdapterPosition