티스토리 뷰

RecyclerView의 item에 Custom View를 넣고 싶을 때

이슈

  • RecyclerView가 10개의 아이템을 가지고 있다고 하였을 때, 0번째 item에서의 Custom View에서 특정 작업을 했던 것이 7번째 item의 Custom View에도 영향을 줍니다.
  • 즉, 별개의 Custom View로 동작해야하는데 서로가 서로에게 영향을 줬습니다.
  • 0번째, 7번째가 서로 영향을 주는 것은 특정 상황에서만 나온 것이고 서로 영향을 주는 객체는 매번 달라졌습니다.

수정 전 코드

지금껏 해왔던대로 RecyclerView의 item 레이아웃(item_layout.xml)에 직접 만든 Custom View를 넣어서 사용함.
(저 같은 경우에는 GraphView를 만들었습니다.)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/item_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <com.mycompany.graph.GraphView
        android:id="@+id/graph_item"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>
  • RecyclerView Adapter의 ViewHolder 클래스는 다음과 같이 정의하였습니다.
class ViewHolder extends RecyclerView.ViewHolder {
  private final RelativeLayout itemLayout;
  private final GraphView graphView;

  ViewHolder(View view) {
    super(view);
    itemLayout = view.findViewById(R.id.item_layout);
    graphView = view.findViewById(R.id.graph_item);
  }
}
  • onBindViewHolder에서 Custom View의 설정을 추가해줬습니다.
@Override
public void onBindViewHolder(@NonNull GraphAdapter.ViewHolder holder, int position) {
  GraphView graphView = holder.graphView;
  graphView.configure();
}

문제

  • 제가 생각하기로는 RecyclerView의 ViewHolder가 재사용하는 과정에서 ViewHolder에서 이미 만들어놨던 Custom View를 활용하면서 서로에게 영향을 준 것 같습니다.
  • 즉, 같은 ViewHolder를 활용하므로 Custom View의 객체도 같은 객체를 활용하기 때문입니다.
  • 실제로 onBindViewHolder에서 position별로 각 holder의 Custom View의 객체 id값을 확인한 결과는 다음과 같습니다.
position 0 1 2 3 4 5 6 7 8 9
id 06f3 d291 2f54 83d1 d194 f8f0 983f 06f3 d291 2f54
  • 위 표를 보시면 0번째, 1번째, 2번째 id값이 각각 7, 8, 9번째 id값에 대응됨을 볼 수 있습니다.

해결

  • RecyclerView.Adapter에서 Custom View의 리스트를 갖고 있다가 onBindViewHolder가 호출될 때마다 각 position에 맞는 holder에 Custom View를 addView하는 식으로 처리하였습니다.

item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/item_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
</RelativeLayout>

CustomAdapter.class

class GraphAdapter extends RecyclerView.Adapter<GraphAdapter.ViewHolder> {
    private List<GraphView> graphViews = new ArrayList<>();
    private Context context;

    GraphAdapter(Context context) {
        this.context = context;
    }

    public void setGraphViews(int num) {
        this.graphViews.clear();
        for (int i = 0; i < num; i++) {
            GraphView graphView = new GraphView(context);
            this.graphViews.add(graphView);
        }
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public GraphAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_graph, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull GraphAdapter.ViewHolder holder, int position) {
        if (graphViews.get(position).getParent() != null) {
            ((ViewGroup) graphViews.get(position).getParent()).removeView(graphViews.get(position));
        }
        holder.itemLayout.addView(graphViews.get(position));
    }

    @Override
    public int getItemCount() {
        return graphViews.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private final RelativeLayout itemLayout;

        ViewHolder(View view) {
            super(view);
            itemLayout = view.findViewById(R.id.item_layout);
        }
    }
}
  • 위 경우에는 Custom View의 개수를 setGraphViews(int num)라는 함수를 통해서 받고 Custom View를 하나씩 생성한 다음에 graphViews라는 List타입의 변수에 넣어둡니다.
  • onBindViewHolder에서 graphViews안에 들어 있는 Custom View를 하나씩 꺼내서 itemLayout(RelativeLayout)에 addView해줍니다.
  • onBindViewHolder에서 holder가 재사용되는 것을 반복하면서 Custom View의 parentView가 달라질 수 있는데 따라서 parent가 이미 배정되어 있는 경우라면 해당 Custom View를 parent에서 해제하기 위해서 아래 코드를 추가하였습니다.
if (graphViews.get(position).getParent() != null) {
    ((ViewGroup) graphViews.get(position).getParent()).removeView(graphViews.get(position));
}

회고

  • 저의 경우에는 위와 같이 해결을 하였지만 성능을 위해서 최적의 방법은 아닐 수 있습니다.
  • 좋은 의견이 있으면 남겨주시면 감사하겠습니다.
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함