본문 바로가기

Android

[Android] 홍드로이드 기초 강의 - 리사이클러뷰(RecyclerView)

1. 안드로이드 리사이클러뷰(RecyclerView)

"수많은 데이터의 집합을 지정된 영역 내에서 유연하게(flexible) 표시되도록 만들어주는 위젯"입니다.

 

안드로이드 개발자 문서에 작성된 리사이클러뷰(RecyclerView)의 설명입니다. 이해를 돋기 위해 설명을 덧붙이자면, "사용자가 관리하는 데이터의 집합을 서로 다른 아이템으로 생성하고 리스트업 해서 한눈에 보이도록 합니다. 뿐만 아니라 스크롤 기능이 구비되어 있어 한 화면에 표시되기 어려운 양의 아이템들도 볼 수 있습니다."

 

이전에는 ListView 위젯으로 여러 아이템들을 표현해주었습니다. RecyclerView가 ListView의 기능을 이어받아 그 기능을 담당하는 것인데, "Recycle"이라는 용어가 붙은 것이 특이합니다. 이는 두 위젯이 동작하는 방식을 살펴보면 단번에 이해할 수 있습니다.

 

 

ListView는 스크롤할 때마다 화면에서 사라지는 가장 위의 아이템을 삭제하고 가장 아래에 새로운 아이템을 생성합니다. 아이템 삭제 및 생성 횟수가 늘어날수록 cost가 매우 높아지게 됩니다.

 

반면 RecyclerView는 가장 위의 아이템을 삭제하지 않고 재활용(recycle)해서 맨 밑으로 옮깁니다. 아이템의 데이터만 수정하고 View[각주:1] 자체는 재활용하는 구조입니다. 즉, 새로운 View 객체를 생성하지 않기 때문에 ListView 대비 많은 cost를 아낄 수 있게 됩니다.

 

2. RecyclerView의 유연함(flexibility)

RecyclerView의 장점으로 꼽히는 것에 "유연함(flexibility)"이 있습니다. 

프로그래밍 분야에서 유연함이란, "구현 요소" 또는 구현에 따른 "결과물"이 쉽게 변경되거나 확장될 수 있음을 의미한다고 합니다. 그렇다면 어떤 점에서 유연함이 보장되는지 ListView와 비교해보겠습니다.

 

ListView의 기본 구현으로 아이템들은 수직 방향으로만 나열됩니다. 수평으로도 나열할 수 있지만 이럴 경우 ListView가 아닌 다른 View를 사용하거나, ListView를 재 구현해야 합니다. 문제는 아무런 가이드도 제공되지 않기 때문에 재구현에 따른 오류 처리들은 오롯이 개발자의 몫이 됩니다.

 

그러나 RecyclerView는 이러한 단점들을 보완했습니다. 수직뿐만 아니라 수평 및 2차원 격자(Grid) 형태로도 아이템들을 나열할 수 있고, 아이템 뷰의 동적(Dynamic) 구성을 용이하게 해 주며, 이를 런타임에 바꿀 수도 있습니다. 이런 특징들이 RecyclerView의 유연함을 나타내는 장점들입니다.

 

3. RecyclerView의 구성 요소

RecyclerView는 데이터 목록을 아이템 단위의 View로 생성해서 화면에 표시하기 위해 Adapter를 사용합니다. Adapter에 의해 생성된 View 객체를 어떤 방식으로 나열할지 결정하기 위해서 Layout Manager를 이용합니다. 끝으로 Layout Manager가 제공하는 형태로 Adapter가 생성한 View를 ViewHolder 객체에 저장되어 화면에 표시하게 됩니다.

 

1) RecyclerView

RecyclerView는 v7 Support Library에서 관리했지만 현재는 androidX Library에서 관리되고 있습니다. 위에서 설명한 요소들을 통해서 사용자 데이터를 리스트 형태로 화면에 표시하는 컨테이너 역할을 수행합니다. 

 

 

2) Adapter

Adapter는 RecyclerView에 표시될 아이템 View를 생성해줍니다.

 

 

3) LayoutManager

LayoutManager는 아이템 View들이 RecyclerView 내부에서 배치되는 형태를 관리합니다.

 

 

배치 형태는 아래와 같습니다.

 

 

4) ViewHolder

ViewHolder는 화면에 표시될 아이템 View를 저장하는 객체입니다. Adapter에 의해 관리되고 필요에 따라 Adapter에서 생성됩니다.

 

4. RecyclerView 예제

기능 설명)

아이템 추가 Button을 누르면 아이템 View를 생성하고 이를 RecyclerView에 표시되도록 만듭니다. 아이템 View는 아래의 이미지처럼 구현하고 수직으로 배치합니다.

 

 

아이템 View를 짧게 누르면 아이템 명이 Toast로 나타나고 반대로 길게 누르면 해당 아이템을 삭제하도록 합니다.

 

1) RecyclerView Layout

main activity에 RecyclerView를 추가합니다.

 

// activity_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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:scrollbarFadeDuration="0"
        android:scrollbarSize="5dp"
        android:scrollbarThumbVertical="@android:color/darker_gray">

    </androidx.recyclerview.widget.RecyclerView>

    <Button
        android:id="@+id/btn_add"
        android:layout_weight="8"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="추가"/>

</LinearLayout>

 

2) RecyclerView 아이템 View Layout

RecyclerView 아이템에 표시될 아이템 View Layout을 추가합니다. Image 1개와 TextView 2개로 구성되어 있습니다.

 

// item_list.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/iv_profile"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"/>

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

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="J-meong"/>

            <TextView
                android:id="@+id/tv_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="리사이클러뷰"/>
        </LinearLayout>
    </LinearLayout>

</LinearLayout>

 

3) 아이템 View의 데이터 클래스

아래와 같이 클래스로 정의해서 변경이 용이하도록 구성합니다.

 

package com.example.recyclerviewexample;

public class MainData {
    private int iv_profile;
    private String tv_name;
    private String tv_content;

    public MainData(int iv_profile, String tv_name, String tv_content) {
        this.iv_profile = iv_profile;
        this.tv_name = tv_name;
        this.tv_content = tv_content;
    }

    public int getIv_profile() {
        return iv_profile;
    }

    public void setIv_profile(int iv_profile) {
        this.iv_profile = iv_profile;
    }

    public String getTv_name() {
        return tv_name;
    }

    public void setTv_name(String tv_name) {
        this.tv_name = tv_name;
    }

    public String getTv_content() {
        return tv_content;
    }

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

 

4) RecyclerView Adapter 구현

RecyclerView에서는 개발자가 Adapter를 직접 구현해야  한다고 했습니다. 이때 새로 만드는 Adapter는 RecyclerView.Adapter를 상속해야 합니다. Adapter를 상속해서 새로 만들 때, 오버라이드가 필요한 메서드가 있습니다.

메소드 설명
onCreateViewHolder(ViewGroup parent, int viewType) viewType형태의 아이템 View를 위한 ViewHolder 객체 생성
onBindViewHolder(ViewHolder holder, int position) position에 해당하는 데이터를 ViewHolder의 아이템 View에 표시
getItemCount() 전체 아이템 개수 리턴

 

다음은 주요 메소드를 구현한 코드입니다.

onCreateViewHolder()

 

@NonNull
@Override
public MainAdapter.CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {   // 아이템 뷰를 위한 뷰홀더 객체를 생성하여 리턴

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
    CustomViewHolder holder = new CustomViewHolder(view);

    return holder;
}

 

onBindViewHolder()

그 외에 추가 기능 및 함수가 정의되어 있습니다.

// position에 해당하는 데이터를 뷰홀더의 아이템뷰에 표시
@Override
public void onBindViewHolder(@NonNull MainAdapter.CustomViewHolder holder, int position) {      // 실제 item이 화면에 추가될 때의 동작
    holder.iv_profile.setImageResource(arrayList.get(position).getIv_profile());
    holder.tv_name.setText(arrayList.get(position).getTv_name());
    holder.tv_content.setText(arrayList.get(position).getTv_content());

    // 태그 지정
    holder.itemView.setTag(position);
    // 아이템 View가 짧게 클릭 됐을 때 아이템명을 띄움
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            String curName = holder.tv_name.getText().toString();   // 클릭한 item의 이름을 가져옴
            Toast.makeText(view.getContext(), curName, Toast.LENGTH_SHORT).show();   //activity가 아니라서 view로부터 가져옴
        }
    });

    // 아이템 View가 롱클릭 됐을 때 item 제거
   holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
       @Override
       public boolean onLongClick(View view) {
           remove(holder.getAdapterPosition());

           return true;
       }
   });
}

// item 제거
public void remove(int position) {
    try {
        arrayList.remove(position);     // 제거
        notifyItemRemoved(position);    // 새로고침
    } catch (IndexOutOfBoundsException ex) {
        ex.printStackTrace();
    }
}

public class CustomViewHolder extends RecyclerView.ViewHolder {
    protected ImageView iv_profile;
    protected TextView tv_name;
    protected TextView tv_content;

    public CustomViewHolder(@NonNull View itemView) {
        super(itemView);
        this.iv_profile = (ImageView) itemView.findViewById(R.id.iv_profile);
        this.tv_name = (TextView) itemView.findViewById(R.id.tv_name);
        this.tv_content = (TextView) itemView.findViewById(R.id.tv_content);
    }
}

 

위의 코드에서 아이템 View를 짧게 혹은 길게 눌렀을 때에 서로 다른 기능이 동작하도록 구현되어 있습니다. 이를 위해서 setTag() 메소드를 활용했습니다. position값으로 클릭된 View를 holder의 itemView로 설정하는 것을 알 수 있습니다.

 

getItemCount()

 

// 전체 데이터 갯수 리턴
@Override
public int getItemCount() {
    return (null != arrayList ? arrayList.size() : 0);
}

 

5) MainActivity

끝으로 MainActivity를 구현해줍니다.

 

package com.example.recyclerviewexample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ArrayList<MainData> arrayList;
    private MainAdapter mainAdapter;
    private RecyclerView recyclerView;
    private LinearLayoutManager linearLayoutManager;


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

        // 리사이클러뷰에 LinearLayoutManager 객체 지정
        recyclerView = (RecyclerView)findViewById(R.id.rv);
        linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);

        arrayList = new ArrayList<>();

        // 리사이클러뷰에 mainAdapter 객체 지정
        mainAdapter = new MainAdapter(arrayList);
        recyclerView.setAdapter(mainAdapter);

        Button btn_add = (Button)findViewById(R.id.btn_add);
        btn_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MainData mainData = new MainData(R.mipmap.ic_launcher, "장도비", "리사이클러뷰");
                arrayList.add(mainData);
                mainAdapter.notifyDataSetChanged();
            }
        });
    }
}

 

6) 결과

구현 결과는 아래와 같으며, 전체 코드는 GitHub를 참조해주시기 바랍니다.

https://recipes4dev.tistory.com/154

 

안드로이드 리사이클러뷰 기본 사용법. (Android RecyclerView)

1. 안드로이드 리사이클러뷰(RecyclerView) 리사이클러뷰(RecyclerView)는, "많은 수의 데이터 집합을, 제한된 영역 내에서 유연하게(flexible) 표시할 수 있도록 만들어주는 위젯"입니다. [안드로이드 개발

recipes4dev.tistory.com

https://developer.android.com/reference/android/support/v7/widget/RecyclerView

 

RecyclerView  |  Android Developers

 

developer.android.com

https://developer.android.com/reference/android/view/View

 

View  |  Android Developers

android.net.wifi.hotspot2.omadm

developer.android.com

https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=ko 

 

RecyclerView로 동적 목록 만들기  |  Android 개발자  |  Android Developers

RecyclerView로 동적 목록 만들기   Android Jetpack의 구성요소 RecyclerView를 사용하면 대량의 데이터 세트를 효율적으로 표시할 수 있습니다. 개발자가 데이터를 제공하고 각 항목의 모양을 정의하면 R

developer.android.com

 

  1. 대화형 UI 구성 요소(버튼, 텍스트 필드 등)를 만드는 데 사용되는 위젯 의 기본 클래스 [본문으로]