본문 바로가기
Android/Concepts

RecyclerView 사용법

by JuHy_ 2020. 5. 4.

RecyclerView란?

RecyclerView란 기본적으로 여러 가지 아이템을 나타내는 면에서는 ListView와 비슷하다.

하지만 ListView의 경우 위 아래 수직 방향으로만 아이템을 배열할 수가 있다.

아이템을 좌우 방향으로 스크롤하여 보여주고 싶다면 RecyclerView를 사용하면 된다.

 

RecyclerView는 이렇게 좌우 방향 스크롤 뿐만 아니라 ListView에 비해 좀 더 확장 가능한 기능을 제공한다.

따라서 ListView와 같은 단순한 형태보다 조금 더 복잡한 기능이 필요할 때 사용하면 된다.

 

또한 RecyclerView는 ViewHolder를 기본적으로 사용하게 되어있다.

ViewHolder란 아이템을 스크롤하여 뷰를 띄워줄 때 매번 inflate하지 않고 저장해두고 사용하기 위한 객체이다.

 

그럼 이제 사용법을 알아보자.

 

※ 이 글은 안드로이드 API 29 버전을 기준으로 구현하였으며 androidx.recyclerview를 사용하였습니다.

 

사용법

implementation 'androidx.recyclerview:recyclerview:1.1.0'

먼저 build.gradle에서 dependencies에 RecyclerView를 import 해준다.

 

package com.juhy.myapplication;

public class Customer {
    public String name;
    public String phone;

    public Customer(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:textColor="#0000FF"
        android:text="Name" />

    <TextView
        android:id="@+id/tv_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#FF0000"
        android:text="Phone" />

</LinearLayout>

먼저 아이템 하나의 정보를 담는 클래스와 이에 대응하는 레이아웃 파일을 만들어준다.

 

package com.juhy.myapplication;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

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

import java.util.ArrayList;

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

    Context context;
    ArrayList<Customer> items = new ArrayList<Customer>();

    public CustomerAdapter(Context context){
        this.context = context;
    }

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

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View itemView = inflater.inflate(R.layout.customer_item, parent, false);

        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Customer item = items.get(position);
        holder.setItem(item);
    }

    public void addItem(Customer item){
        items.add(item);
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView tv_name;
        TextView tv_phone;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            tv_name = itemView.findViewById(R.id.tv_name);
            tv_phone = itemView.findViewById(R.id.tv_phone);
        }

        public void setItem(Customer item){
            tv_name.setText(item.name);
            tv_phone.setText(item.phone);
        }
    }

}

이제 ListView와 마찬가지로 데이터와 RecyclerView를 이어줄 Adapter를 만들어주자.

Adapter는 다음과 같은 순서로 동작하게 된다.

 

1. 아이템이 보여지면 ViewHolder가 생성되며 레이아웃 파일을 inflate 한다. (onCreate)

2. ViewHolder 생성자에서 findViewById를 통해 View들을 inflate 한다. 

3. ViewHolder와 아이템이 연결되며 View들에 데이터를 설정한다. (onBind)

 

따라서 이에 따라 다음 메소드들을 정의해주면 된다.

 

CustomerAdapter() - 생성자 호출 시 Context를 넘겨준다.

getItemCount() - 아이템의 수를 반환해준다.

 

onCreateViewHolder() - ViewHolder가 만들어지는 시점 / 레이아웃을 inflate

onBindViewHolder() - ViewHolder와 아이템이 연결되는 시점 / ViewHolder에 아이템 지정

 

ViewHolder() - 아이템 레이아웃의 View들을 findViewById를 통해 객체화

ViewHolder.setItem() - 아이템 레이아웃의 View들의 값을 설정

 

package com.juhy.myapplication;

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

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    RecyclerView recyclerView;

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

        recyclerView = findViewById(R.id.recyclerView);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);

        CustomerAdapter adapter = new CustomerAdapter(getApplicationContext());

        adapter.addItem(new Customer("Mark", "010-1234-5678"));
        adapter.addItem(new Customer("Ted", "010-1111-2222"));
        adapter.addItem(new Customer("JuHy", "010-1212-3434"));

        recyclerView.setAdapter(adapter);
    }

}

Activity에서는 먼저 findViewById()를 통해 RecyclerView를 찾아준다.

 

그리고 LayoutManager를 통해 아이템들을 보여줄 형식을 지정해준다.

LinearLayoutManager.HORIZONTAL은 좌우 형태로 아이템들을 보여주는 형태이다.

 

그리고 Adapter를 생성하여 데이터를 추가해 준 뒤 RecyclerView에 지정해주면 된다.

 

실행해보면 좌우 스크롤을 통해 아이템이 나타나는 것을 볼 수 있다.

 

OnItemClickListener 추가하기

RecyclerView.Adapter에는 setOnItemClickListener()가 정의되어 있지 않기 때문에 직접 구현해야 한다.

 

OnItemClickListener listener;

public void setOnItemClickListener(OnItemClickListener listener){
    this.listener = listener;
}

public static interface OnItemClickListener{
    public void onItemClick(ViewHolder holder, View view, int position);
}

먼저 OnItemClickListener interface와 이를 설정할 setOnItemClickListener() 함수를 만들어주자.

 

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    Customer item = items.get(position);
    holder.setItem(item);

    holder.setOnItemClickListener(listener);
}

static class ViewHolder extends RecyclerView.ViewHolder {
    TextView tv_name;
    TextView tv_phone;

    OnItemClickListener listener;

    public ViewHolder(@NonNull View itemView) {
        super(itemView);

        tv_name = itemView.findViewById(R.id.tv_name);
        tv_phone = itemView.findViewById(R.id.tv_phone);

        itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = getAdapterPosition();

                if(listener != null){
                    listener.onItemClick(ViewHolder.this, v, position);
                }
            }
        });
    }

    public void setItem(Customer item){
        tv_name.setText(item.name);
        tv_phone.setText(item.phone);
    }

    public void setOnItemClickListener(OnItemClickListener listener){
        this.listener = listener;
    }
}

그리고 ViewHolder 클래스 내에 setOnItemClickListener() 메소드를 만들어 listener를 입력받도록 한 뒤,

생성자에서 원하는 View에 setOnClickListener()를 통해 클릭 시 onItemClick을 실행시켜준다.

 

마지막으로 onBind 시 ViewHolder에 setOnItemClickListener()를 통해 listener를 등록시켜주면 된다.

 

adapter.setOnItemClickListener(new CustomerAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(CustomerAdapter.ViewHolder holder, View view, int position) {
        Customer item = adapter.getItem(position);
        Toast.makeText(getApplicationContext(), item.name + " clicked", Toast.LENGTH_SHORT).show();
    }
});

이제 액티비티로 돌아와 adapter의 setOnItemClickListener()를 통해 클릭 시 이벤트를 정의하면 된다.

 

아이템 클릭 시 정상적으로 이름이 Toast로 나타나는 것을 볼 수 있다.

 

 

Reference

[부스트코스]안드로이드 프로그래밍

https://www.edwith.org/boostcourse-android