Dico

[Android] RecyclerView

  • 민갤

RecyclerView

ListView 보다 더 편리하고 유연하게 만들어 진 ViewGroup의 서브 클래스

하나의 자식 View 객체를 각 항목당 배치하여 리스트를 보여준다는 것은 ListView와 같다.

다른 점은 ListView는 직접 코딩 하지 않으면 모든 항목을 한 번에 보여주지만, 

RecyclerView는 별도의 코딩 없이 화면을 채울 정도의 View 객체만 생성 후 

화면이 스크롤 되면 화면을 벗어나는 View를 재활용하여 다음 View 객체를 보여준다.

RecyclerView는 LayoutManager가 함께 있어야 움직이고, 

View 객체를 얻으려면 Adapter뿐만 아니라 ViewHolder가 반드시 필요하다.

그리고 자신이 View 객체를 생성하지 않고 Adapter를 통해 ViewHolder 객체를 생성하고 사용한다.

RecyclerView 라이브러리를 추가하기

Library dependency로 recyclerview를 찾아서 추가하거나

(File>Project Structure>app>Dependencies>추가(+) 버튼>Library Dependency)

build.gradle(Module: app)의 dependencies에 직접 추가한다.

implementation 'com.android.support:recyclerview-v7:27.0.2'

RecyclerView의 버전은 appcompat과 일치시킨다.

implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'

activity_main.xml

RecyclerView를 layout 안에 위치시킨다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    tools:context="[PackageName].MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/view_recycler"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
<!-- 방법2 -->
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/view_recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="[PackageName].MainActivity">

</android.support.v7.widget.RecyclerView>

item_recycler.xml

Layout의 height을 wrap_content로 설정하고 객체들의 높이를 지정해주어야 리스트 항목들이 제대로 보인다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="1dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="80dp"
        android:layout_height="50dp"
        android:gravity="center"
        android:padding="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tv_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:background="#e5e5e5"
        android:gravity="center_vertical"
        android:padding="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/tv_title"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

RecycleItem.java

데이터 정보를 갖는 class 생성.

public class RecycleItem {
    private String title;
    private String content;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

MainActivity.java

RecyclerView 객체를 참조하는 변수에 LayoutManager를 설정한다.

LayoutManager는 리스트 항목의 배치를 어떻게 할지 정하고, 스트롤 동작도 정의한다.

수평/수직 리스트 LinearLayoutManager, 그리드 리스트 GridLayoutManager, 

높이가 불규칙적인 그리드 리스트 StaggeredGridLayoutManager를 기본적으로 제공한다.

이 것을 설정해주지 않으면 RecyclerView는 동작하지 않는다.

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

public class MainActivity extends AppCompatActivity {

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

        RecyclerView mRecyclerView = findViewById(R.id.view_recycler);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}

RecycleAdapter.java

컨트롤러 객체인 Adapter는 RecyclerView와 RecyclerView의 데이터 사이에 위치하여 모든 데이터를 관리한다.

List는 순차 리스트를 지원하는 인터페이스로, 지정된 타입의 객체가 저장된다.

Adapter 인스턴스에 입력되는 List로 View 객체들의 정보를 입력한다.

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

public class RecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    List<RecycleItem> items;

    public RecycleAdapter(List<RecycleItem> items) {
        this.items = items;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View v = inflater.inflate(R.layout.item_recycler, parent, false);
        return null;
    }

    // View가 재사용될 때마다 호출된다.
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    }

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

    리스트의 각 항목을 이루는 XML(디자인) 적용. 

    리스트 항목으로 보여줄 View(XML)를 inflate하여 그 View의 객체를 참조한 ViewHolder를 생성한다.

    아직 ViewHolder 클래스를 만들지 않아서 반환 값을 null로 두었다.

  • onBindViewHolder

    리스트의 각 항목에 들어갈 데이터 지정.

    (RecyclerView.ViewHolder) holder를 통해 ViewHolder를 참조하여 참조된 객체에 데이터 정보를 넣는다.

    이 메서드도 viewHolder를 생성 후 사용할 것이다.

  • getItemCount

    화면에 보여 줄 데이터의 개수를 반환한다.

RecyclerAdapter.java - Inner Class RecycleHolder

RecycleAdapter  안에 RecyclerView.ViewHolder를 상속받는 내부 클래스를 생성한다.

ViewHolder는 리스트에서 하나의 항목에 해당하는 View를 보존한다.

Adapter의 onCreateViewHolder가 ViewHolder를 생성하면서 전달한 View를 itemView가 받아 그 View의 객체를 가져온다. 

객체는 itemView.findViewById()를 통해 가져와서 참조한다.

    private class RecycleHolder extends RecyclerView.ViewHolder {
        private TextView tvTitle, tvContent;

        public RecycleHolder(View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tv_title);
            tvContent = itemView.findViewById(R.id.tv_content);
        }
    }

RecyclerAdapter.java

onCreateViewHolder에서 반환한 ViewHolder가 onBindViewHolder의 매개변수 holder로 전달된다.

holder를 이용하여 리스트 항목에 데이터 정보를 넣는다. 

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

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ...
        return new RecycleHolder(v);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        RecycleItem item = items.get(position);

        RecycleHolder rHolder = (RecycleHolder) holder;
        rHolder.tvTitle.setText(item.getTitle());
        rHolder.tvContent.setText(item.getContent());
    }

    ...
}
  • 전체 소스
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

public class RecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    List<RecycleItem> items;

    public RecycleAdapter(List<RecycleItem> items) {
        this.items = items;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View v = inflater.inflate(R.layout.item_recycler, parent, false);
        return new RecycleHolder(v);
    }

    // View가 재사용될 때마다 호출된다.
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        RecycleItem item = items.get(position);

        RecycleHolder rHolder = (RecycleHolder) holder;
        rHolder.tvTitle.setText(item.getTitle());
        rHolder.tvContent.setText(item.getContent());
    }

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

    private class RecycleHolder extends RecyclerView.ViewHolder {
        private TextView tvTitle, tvContent;

        public RecycleHolder(View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tv_title);
            tvContent = itemView.findViewById(R.id.tv_content);
        }
    }
}

MainActivity.java

데이터가 담긴 item을 list에 담으면 하나의 리스트 항목이 만들어진다.

그렇게 만들어진 리스트를 Adapter에 전달하면 ViewHolder를 통해 리스트가 그려진다.

public class MainActivity extends AppCompatActivity {

    private RecycleAdapter adapter;

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

        RecyclerView mRecyclerView = findViewById(R.id.view_recycler);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

        if (adapter == null) {
            adapter = new RecycleAdapter(setList());
            mRecyclerView.setAdapter(adapter);
        }
    }

    public List<RecycleItem> setList() {
        List<RecycleItem> items = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            RecycleItem item = new RecycleItem();
            item.setTitle("제목" + i);
            item.setContent("내용" + i);
            items.add(item);
        }
        return items;
    }
}