이전에 정리한 ListView는 항목이 갱신될 때마다 매번 아이템 뷰를 새로 구성해야 한다. 이것은 많은 데이터를 표시하는데 있어 성능 저하를 유발할 수 있다. 이를 해결하기 위해 나온 RecyclerView는 아이템을 표시하기 위해 뷰를 재활용하고, 이를 위해 뷰홀더(ViewHolder)패턴을 사용하도록 만들어져있다.
ListView는 기본적으로 아이템들을 수직 방향으로만 나열할 수 있다. 그리고 아이템 뷰를 동적으로 구성하기 어려웠다.
이에 비해 RecyclerView는 수직, 수평 방향으로 아이템들이 나열되게 만들 수 있고, 아이템 뷰의 동적 구성을 용이하게 해주며 이를 런타임에 바꿀 수도 있다.
그러면 RecyclerView에 대해 더 자세히 알아보고 어떻게 사용할 수 있는지에 대해 알아보도록 하자.
앞에서 ListView와 차이점을 설명하면서 RecyclerView의 특성에 대해 설명했기 때문에 간단하게 정리하도록 하겠다.
RecyclerView는 사용자가 관리하는 많은 데이터의 집합을 개별 아이템 단위로 구성하여 화면에 출력하는 뷰그룹이며, 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤이 가능한 리스트로 표시해주는 위젯이다.
RecyclerView는 ListView와 사용 목적 및 동작 방식이 유사하지만, RecyclerView가 더 좋은 성능을 가지고 있다고 생각할 수 있다.
RecyclerView는 데이터 목록을 아이템 단위의 뷰로 구성하여 화면에 표시하기 위해 Adapter를 사용한다. (이는 ListView와 같다.)
또, 리사이클러뷰는 다양한 형태의 레이아웃으로 아이템 뷰를 나열할 수 있다. 이를 위해 아이템 뷰가 나열되는 형태를 관리하기 위한 요소를 제공하는데, 이를 레이아웃 매니저라고 한다.
마지막으로 레이아웃 매니저가 제공하는 레이아웃 형태로 어댑터를 통해 만들어진 각 아이템 뷰는 뷰홀더 객체에 저장되어 화면에 표시되고, 필요에 따라 생성 또는 재활용한다.
RecyclerView
사용자 데이터를 리스트 형태로 화면에 표시하는 컨테이너 역할
Adapter
RecyclerView에 표시될 아이템 뷰를 생성하는 역할(사용자 데이터 리스트로부터 아이템 뷰를 생성한다.)
Layout Manager
RecyclerView가 아이템을 화면에 표시할 때 아이템 뷰들이 RecyclerView내부에 배치되는 형태를 관리한다.
ViewHolder
화면에 표시될 아이템 뷰를 저장하는 객체로, Adapter에 의해 관리된다.
미리 생성된 뷰홀더 객체가 있는 경우 새로 생성하지 않고 이미 만들어져 있는 뷰홀더를 재활용하는데, 이 때는 단순히 데이터가 뷰홀더의 아이템 뷰에 바인딩된다.
그럼 이제 RecyclerView의 사용방법에 대해 알아보자.
activity_main.xml 파일(레이아웃)에 RecyclerView를 추가한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
RecyclerView 아이템에 표시될 아이템 뷰 레이아웃을 추가한다.
예제로 작성하는 것이기 때문에 아이템 뷰는 간단하게 텍스트 뷰 위젯 하나만 가진다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView" />
</LinearLayout>
ListView의 경우 보통 BaseAdapter클래스를 상속 받아 Adapter를 만들었지만 RecyclerView에서는 개발자가 Adapter를 직접 구현해야 한다. 그리고 이 때 만드는 Adapter는 RecyclerView.Adapter
를 상속하여 구현해야 한다.
RecyclerView.Adapter
를 상속 받아 새로운 Adapter를 구현할 때 오버라이드가 필요한 메서드는 다음과 같다.
onCreateViewHolder(ViewGroup parent, int viewType)
viewType 형태의 아이템 뷰를 위한 뷰홀더 객체 생성
onBindViewHolder(ViewHolder holder, int position)
position에 해당하는 데이터를 뷰홀더의 아이템뷰에 표시
getItemCount()
전체 아이템 개수 리턴
package com.example.recyclerview;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class SimpleTextAdapter extends RecyclerView.Adapter<SimpleTextAdapter.ViewHolder> {
private ArrayList<String> items = null;
public class ViewHolder extends RecyclerView.ViewHolder{
TextView textView;
ViewHolder(View itemView){
super(itemView);
// 뷰 객체에 대한 참조
textView = itemView.findViewById(R.id.textView);
}
}
// 생성자
SimpleTextAdapter(ArrayList<String> list){
items = list;
}
// 아이템 뷰를 위한 뷰홀더 객체 생성하여 리턴
@Override
public SimpleTextAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.item, parent, false);
SimpleTextAdapter.ViewHolder vh = new SimpleTextAdapter.ViewHolder(view);
return vh;
}
// position에 해당하는 데이터를 뷰홀더의 아이템 뷰에 표시
@Override
public void onBindViewHolder(SimpleTextAdapter.ViewHolder holder, int position) {
String text = items.get(position);
holder.textView.setText(text);
}
// 전체 데이터 개수 리턴
@Override
public int getItemCount() {
return items.size();
}
}
RecyclerView.Adapter를 상속받는 SimpleTextAdapter Adapter를 만들었다.
Adapter 내에서 뷰홀더를 위한 클래스를 구현했다.
그리고 뷰홀더에서는 아이템에 표시될 텍스트뷰에 대한 참조를 갖도록 만들었다.
이렇게 작성한 뷰홀더는 Adapter의 onCreateViewHolder()
와 onBindViewHolder()
메서드를 통해 각각 생성 및 바인딩되어 화면에 표시된다.
package com.example.recyclerview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> list = new ArrayList<>();
for(int i = 0; i < 100; i++){
list.add(String.format("Text %d", i));
}
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
SimpleTextAdapter adapter = new SimpleTextAdapter(list);
recyclerView.setAdapter(adapter);
}
}
레이아웃 매니저를 지정할 때 LinearLayoutManager
객체를 사용했다. 이는 화면을 수직 방향으로 나열되도록 설계하였기 때문이고, LinearLayoutManager
는 orientation 기본 값이 “VERTICAL”이므로 객체를 생성하면 수직 방향으로 아이템을 표시하게 된다.
만약 수평 방향으로 배치하고 싶다면 아래 코드처럼 객체 생성 시 아이템 배치 방향을 바꿔주면 된다.
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)) ;
실행 시 for문으로 넣어준 Text i가 각 아이템으로 추가된 것을 확인할 수 있다.
[1] https://recipes4dev.tistory.com/154 (이론 내용)
[2] https://velog.io/@haero_kim/Android-ViewHolder-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%93%B0%EB%8A%94-%EC%9D%B4%EC%9C%A0 (이론 내용)
[3] https://dev-imaec.tistory.com/27 (코드 참고)