안드로이드

[Android] RecyclerView

빡수수 2022. 11. 30. 21:15

RecyclerView란?

RecyclerView는 ListView와 유사한 형태로 대량의 데이터 세트를 효율적으로 표현할 수 있는 요소이다. 개발자가 데이터를 제공하고 각 항목의 모양을 정의하면 RecyclerView 라이브러리가 필요할 대 요소를 동적으로 생성한다.
이름에서 알 수 있듯이 RecyclerView는 개별 요소를 재활용한다. 항목이 스크롤되어 화면에서 벗어나더라도 RecyclerView는 뷰를 제거하지 않고 새 항목의 뷰를 재사용한다. 이렇게 뷰를 재사용하면 앱의 응답성을 개선하고 전력 소모를 줄이기 때문에 성능이 개선된다.
위의 설명이 RecyclerView와 ListView의 가장 큰 차이점이다. 그래서 실제로 안드로이드 개발을 하다 보면 RecyclerView가 많이 사용된다.

RecyclerView 구현 단계

RecyclerView를 사용하려면 아래의 작업을 수행해야 한다.

  • 목록 또는 그리드의 모양을 결정한다. 대개는 RecyclerView 라이브러리의 표준 레이아웃 관리자 중 하나를 사용할 수 있다.
  • 목록에 있는 각 요소의 모양과 동작 방식을 설계한다. 이 설계에 따라 ViewHolder 클래스를 확장한다. 사용 중인 ViewHolder 버전은 목록 항목에 필요한 모든 기능을 제공한다. ViewHolder는 View의 래퍼이고 그 뷰는 RecyclerView로 관리된다.
  • 데이터를 ViewHolder 뷰와 연결하는 Adapter를 정의한다.

레이아웃

RecyclerView의 항목은 LayoutManager 클래스를 통해 정렬된다. RecyclerView 라이브러리는 가장 일반적인 레이아웃 상황을 처리하는 3가지 레이아웃 관리자를 제공한다.

  • LinearLayoutManager는 항목을 1차원 목록으로 정렬한다.
  • GridLayoutManager는 모든 항목을 2차원 그리드로 정렬한다.
  • StaggeredGridLayoutManager는 GridLayoutManager와 유사하지만, 행의 항목이 동일한 높이거나 동일한 열의 항목이 동일한 너비일 필요가 없다. 결과적으로 행 또는 열의 항목이 서로 오프셋 상태가 될 수 있다.
더보기

그리드가 세로로 정렬된 경우 GridLayoutManager는 각 행의 모든 요소를 동일한 너비와 높이로 만들려고 하지만 행마다 높이가 다를 수 있다. 그리드가 가로로 정렬되는 경우 GridLayoutManager는 각 열의 모든 요소를 동일한 너비와 높이로 만들려고 열마다 너비가 다를 수 있다.

개별 항목의 레이아웃도 디자인해야 한다. 위의 레이아웃들은 필요에 따라 선택해서 사용하면 된다.

어댑터 및 뷰 홀더 구현

레이아웃을 결정했다면, Adapter 및 ViewHolder를 구현해야 한다.
이 두 클래스가 함께 작동하여 데이터 표시 방식을 정의한다. ViewHolder는 목록의 개별 항목 레이아웃을 포함하는 View의 래퍼이다. Adapter는 필요에 따라 ViewHolder 객체를 만들고 이러한 뷰에 데이터를 설정하기도 한다.
뷰를 데이터에 연결하는 프로세스를 Binding(바인딩)이라고 한다.

어댑터를 정의할 때 세 가지 메서드를 재정의해야 한다.

더보기

- onCreateViewHolder() : RecyclerView는 ViewHolder를 새로 만들어야 할 때마다 이 메서드를 호출한다. 이 메서드는 ViewHolder와 그에 연결된 View를 생성하고 초기화하지만 뷰의 컨텐츠를 채우지는 않는다. ViewHolder가 아직 특정 데이터에 바인딩된 상태가 아니기 때문이다.
- onBindViewHolder() : RecyclerView는 ViewHolder를 데이터와 연결할 때 이 메서드를 호출한다. 이 메서드는 적절한 데이터를 가져와서 그 데이터를 사용하여 뷰 홀더의 레이아웃을 채운다. 예를 들면 RecyclerView가 이름 목록을 표시하는 경우 메서드는 목록에서 적절한 이름을 찾아 뷰 홀더의 TextView 위젯을 채울 수 있다.
- getItemCount() : RecyclerView는 데이터 세트 크기를 가져올 때 이 메서드를 호출한다. 예를 들어 주소록 앱에서는 총 주소 개수가 여기에 해당할 수 있다. RecyclerView는 이 메서드를 사용하여 항목을 추가로 표시할 수 없는 상황을 확인한다. Index Error가 발생하면 꼭 getItemCount()를 확인해보자.

 

간단한 예제

1. main XML 구성

<?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"
    android:orientation="vertical"
    android:gravity="center"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RecyclerView Test"
        />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerV"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="10dp"
        android:layout_marginTop="10dp"
        />
</LinearLayout>


2. RecyclerView item XML 작성

<?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"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="10dp"
    android:background="@color/black"
    >

    <TextView
        android:id="@+id/textV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RecyclerView test"
        android:textColor="@color/white"
        />
</LinearLayout>


3. MainActivity Class 작성

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerV;
    ArrayList<String> list = new ArrayList<String>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        list.add("사과");
        list.add("딸기");
        list.add("바나나");
        list.add("수박");
        list.add("참외");
        list.add("메론");
        list.add("파인애플");
        list.add("토마토");
        list.add("배");
        list.add("감");
        list.add("홍시");

        recyclerV = findViewById(R.id.recyclerV);
        recyclerV.setLayoutManager(new LinearLayoutManager(this));
        CustomAdapter customAdapter = new CustomAdapter(this, list);
        recyclerV.setAdapter(customAdapter);
    }
}


4. Adapter Class 작성

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {

    Context mContext;
    ArrayList<String> mList = new ArrayList<String>();

    public CustomAdapter(Context context, ArrayList<String> list) {
        this.mContext = context;
        this.mList = list;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        TextView textV;

        ViewHolder(View itemView) {
            super(itemView);

            textV = itemView.findViewById(R.id.textV);
        }
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) ;

        View view = inflater.inflate(R.layout.item_recyclerview, parent, false) ;
        CustomAdapter.ViewHolder vh = new CustomAdapter.ViewHolder(view) ;
        return vh;
    }

    @SuppressLint("RecyclerView")
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.textV.setText(mList.get(position));

        //클릭 시 Toast 출력
        holder.textV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(mContext, mList.get(position)+"를 클릭하셨습니다!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mList.size(); //특별한 경우가 아니라면 list의 사이즈
    }
}


위의 설명은 기본적인 RecyclerView의 구조와 필수 메서드에 관한 내용이다. 실제로 RecyclerView로 개발을 하다 보면 ViewHolder를 두 개 이상 사용하거나, Header, Footer를 추가하는 등 다양한 작업이 필요한 경우가 있다.
위의 기본구조를 적절히 변형하여 구현하는 연습을 해둘 계획이다. getItemViewType() 메서드도 알아두면 RecyclerView를 좀 더 다양하게 활용할 수 있을 것이다.

 

참조