Android - RecyclerView

MUNGI JO·2024년 2월 6일

Android 공부

목록 보기
1/8
post-thumbnail

RecyclerView란?

대량의 데이터 셋의 개별 요소를 재활용하여 효율적으로 표시할 수 있는 ViewGroup이다.

안드로이드 개발자 공식 문서의 내용으로 보면 RecyclerView의 경우 가장 위의 아이템이 사라지는 것이 아닌 가장 아래로 이동하게 되고 아이템의 data만 수정되게 된다 이것이 가능한 이유는 항목이 Scroll되어 화면에서 벗어나도 View를 제거하지 않고 Scroll 된 새 항목의 View를 재사용하기 때문이다.

기존에 사용되던 ListView는 화면에서 사라지는 가장 위의 아이템을 삭제하고 가장 아래에 새로운 아이템을 형성하는 구조로 아이템이 새롭게 형성 될 때 마다 비용이 소모되기에 반복되는 횟수에 따라서 자원 소모가 심해질 수 밖에 없다. 하지만 RecyclerView를 사용하게 되면 이러한 문제를 해결할 수 있다.

사진 출처 = Android 홍드로이드 기초 강의 - 리사이클러뷰

즉, ListView와 달리 새롭게 View를 생성해야 할 필요가 없어지므로 적은 비용으로 ListView보다 더 유연한 효과를 낼 수 있다. 따라서 이제 ListView를 사용할 일은 없을 듯 하다.

RecyclerView의 유연함

여기서 말하는 유연함이란 데이터의 변경이나 확장이 용이하다는 뜻이라고 생각한다. 그럼 RecyclerView에서 이런 유연함이 어떻게 적용되는 지 살펴보면, ListView의 경우 오직 수직으로만 아이템이 나열된다. 수평으로 나열 시키려면 ListView 커스텀을 통해 재 구현, 혹은 다른 View를 사용할 필요가 있다.

RecyclerView는 ListView가 가지는 단점들을 보완하고자 나온 만큼
1. 수직, 수평 지원됨
2. 2차원 격자 형태로 아이템 나열이 가능
3. 아이템 View의 동적 구성을 용이하게 만들어서 RunTime 시간에 View의 구성을 바꿀 수 있다.

이러한 장점을 가진 RecyclerView의 동작에 대해서 자세히 알아보면

  • 하나의 RecyclerView를 만들기 위해선 여러 클래스가 함께 사용된다.
  1. 우선 RecyclerView는 View를 상속받는 ViewGroup이므로 Layout에 추가해서 사용하면 된다.

  2. 목록의 각 개별 요소는 뷰 홀더 객체로 정의되고 뷰 홀더가 생성 되었을 때는 연결된 데이터가 없으므로 RecyclerView가 뷰 홀더를 뷰의 데이터에 바인딩(뷰 홀더를 뷰의 데이터를 표시하는 곳에 접목)한다. (RecyclerView.ViewHolder 로 가능하다.)

  3. RecyclerView는 View를 요청 후 어댑터에서 메서드를 호출하여 View를 View의 데이터에 바인딩 한다. (RecyclerView.Adapter 를 확장하여 정의 가능하다.) 즉, RecyclerView는 Adapter를 사용하여 View에 데이터 바인딩을 시켜야 데이터를 표시할 수 있다.

  4. LayoutManager로 목록의 개별 요소를 정렬한다. LayoutManager는 RecyclerView 라이브러리에서 제공하는 LayoutManager 중 하나를 사용하거나 직접 정의 가능하다. 모든 LayoutManager는 LayoutManager라는 추상 클래스를 기반으로 한다.

RecyclerView는 클래스 이름이면서 라이브러리 이름이기도 하다.

RecyclerView 사용 - 구성요소

RecyclerView에 사용되는 구성 요소로 3가지 항목은 위에 나열한 것 처럼
1.RecyclerView 2.Adapter 3.LayoutManager 3가지가 필요하다.

1) RecyclerView
Adapter, LayoutManager, DataList 등의 요소를 통해 최종적으로 화면에 표시하는 container 역할을 수행하는 View이다.

2) Adapter
RecyclerView에 데이터를 부착시키는 역할을 수행한다. DataList의 Data는 우선적으로 Adapter를 통해 RecyclerView에 부착되는 것이다. 개발자가 직접 만들어야 하며 RecyclerViwe.Adapter를 상속해야 한다. 또한 새로 만들 때 Override를 해야하는 메소드가 존재한다. Alt+Insert로 간단히 추가 가능하다.

3) LayoutManager
아이템 View들이 RecyclerView 내부에서 배치되는 형태를 관리하는 역할을 한다. 기본적으로 제공되는 Layout은 LinearLayout(수직,수평 가능), GridLayoutManager, StaggeredGridLayoutManager가 있다.

4) View Holder
화면에 표시될 아이템 View를 저장하는 객체다. Adapter에 의해 관리되며 필요에 따라 Adapter에서 생성된다.

5) DataList
그냥 DataList다. CloudDatabase에서 얻어오거나 만들어서 사용하거나 그에 맞춰서 코드를 작성하면 된다. 참고로 getter&setter 작성 시 alt + insert 키 누르면 한 번에 모든 변수에 대해 get set을 만들 수 있다.

이 3가지 요소를 통해 아래 3가지 항목을 작성하면 되는데

RecyclerView를 사용하기 위해선 3가지 항목이 필요하다.
구현 방법 요약 -
1. RecyclerView를 담을 Layout 작성 (activity_Layout)
2. 각 아이템 View 구성을 위한 Layout 작성 (recyclerview_item.xml)
3. RecyclerView Adapter와 ViewHolder 구현 (CustomAdapter.java)
내용 출처 : 티스토리

main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:id="@+id/maincontainer"
    android:padding="1dp"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="92dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabLayout" />
</androidx.constraintlayout.widget.ConstraintLayout>

recycler_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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/itemImage"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginEnd="10dp"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tvItemText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="android" />

            <TextView
                android:id="@+id/tvItemTextSub"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="recyclerViewTest" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

Item.java

public class Item {
    private String tv_ItemTextSub;
    private String tv_ItemText;
    private int itemImage;

    public String getTv_ItemTextSub() {
        return tv_ItemTextSub;
    }

    public void setTv_ItemTextSub(String tv_ItemTextSub) {
        this.tv_ItemTextSub = tv_ItemTextSub;
    }

    public String getTv_ItemText(){
        return tv_ItemText;
    }

    public void setTv_ItemText(String tv_content) {
        this.tv_ItemText = tv_content;
    }

    public int getItemImage() {
        return itemImage;
    }

    public void setItemImage(int itemImage) {
        this.itemImage = itemImage;
    }

    public Item(String tv_ItemTextSub, String tv_content, int itemImage){
        this.tv_ItemTextSub = tv_ItemTextSub;
        this.tv_ItemText = tv_content;
        this.itemImage = itemImage;
    }
}
  • CustomAdapter.Java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
    ArrayList<Item> items;

    // View Holder에 보여줄 아이템들
    public static class ViewHolder extends RecyclerView.ViewHolder {
        private final TextView tvItemText;
        private final TextView tvItemTextSub;
        private final ImageView itemImage;
        public ViewHolder(View view) {
            super(view);
            // Define click listener for the ViewHolder's View
            // 여기서 ViewHolder의 뷰에 대한 클릭 리스너에 대해 정의할 수 있음.
            tvItemText = view.findViewById(R.id.tvItemText);
            tvItemTextSub = view.findViewById(R.id.tvItemTextSub);
            itemImage = view.findViewById(R.id.itemImage);
        }
    }

    // public 생성자로 Adapter를 사용하는 액티비티의 context와 dataset가져옴.
    public CustomAdapter(ArrayList<Item> items) {
        this.items = items;
    }

    // Create new views (invoked by the layout manager)
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        // Create a new view, which defines the UI of the list item
        // 여기서 UI에 리스트에 있는 아이템을 보여줄 새로운 뷰를 만든다.

        // 즉, 아래는 recycler_view_layout을 동적으로 추가하는 것.
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_item, viewGroup, false);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }


    // 아래 함수에서 적절한 데이터를 찾아서 삽입
    // 위의 createViewHolder에서 추가된 Layout에 onBindViewHolder를 통해 데이터를 표시할 수 있다.
    //
    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, final int position) {
        // Get element from your dataset at this position and replace the
        // contents of the view with that element
        // 얘네가 담기는 View가 itemView임. 그래서 item.View를 통해 클릭 효과를 줄 수 있음.
        // viewholder에 가져온 view에다가 바로 dataset을 세팅하는 방법. position에 따라서 다른 값을 가짐.
        Item item = items.get(position); // item객체의 ArrayList인 itmes에서 position에 따라 하나씩 가져옴.
        viewHolder.tvItemText.setText(item.getTv_ItemText());
        viewHolder.tvItemTextSub.setText(item.getTv_ItemTextSub());
        viewHolder.itemImage.setImageResource(item.getItemImage());
        viewHolder.itemView.setTag(position); // 각각의 아이템 마다 position 태그가 달리게 됨.
    }

    public void remove(int position){
        try{
            items.remove(position); //제거
            notifyItemRemoved(position); //새로고침
        } catch (IndexOutOfBoundsException ex){
            ex.printStackTrace();
        }
    }
    // 데이터 세트 크기를 가져올 때 이 함수 사용. 이 함수로 추가로 표시할 수 없는 상황을 확인 가능.
    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() { return items.size(); }
}

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.FrameLayout;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private CustomAdapter adapter;
    private ArrayList<Item> itemList;
    private TabLayout tabLayout;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 데이터 생성
        itemList = new ArrayList<>();
        itemList.add(new Item("text2", "text1", R.drawable.ic_launcher_foreground));
        itemList.add(new Item("text3", "text4", R.drawable.ic_launcher_foreground));

        // RecyclerView 초기화
        recyclerView = findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        // Adapter 설정
        adapter = new CustomAdapter(itemList);
        recyclerView.setAdapter(adapter);
         
    }
}

참고 사이트 :
Android developer
KADOSHoly
밥 먹고 코딩

profile
안녕하세요. 개발에 이제 막 뛰어든 신입 개발자 입니다.

0개의 댓글