반응형

해당 예제의 전체 코드는 아래 링크에서 확인이 가능합니다.

github.com/kkw564/Android/tree/master/recyclerviewtest

 

kkw564/Android

안드로이드 연습 프로젝트 진행. Contribute to kkw564/Android development by creating an account on GitHub.

github.com


 

리사이클러뷰란?

 

이전에는 ListView라는 것이 Android에서 존재했었고, 리스트뷰를 통해 여러 목록을 표현해주는데 사용했다.

하지만 리스트뷰를 스크롤할 때마다 화면에서 사라지는 가장 위의 아이템을 삭제하고 가장 아래에 아이템을 생성하면 횟수가 많아질수록 생성과 삭제의 반복으로 인해 cost가 매우 높아지게 된다

 

따라서 이를 해결하기 위해 Recycler view가 등장했고, 이 리사이클러 뷰는 가장 위의 아이템을 삭제하지 않고 가장 아래의 아이템쪽으로 객체를 이동시켜 재사용을 할 수 있도록 고안되었다.

즉 뷰 객체 자체를 재사용 하는 것인데, 중요한 점은 뷰 객체를 재사용 할 뿐이지 뷰 객체가 담고 있는 데이터(아이템에 존재하는 데이터)는 새로 갱신된다는 것이다.

하지만 뷰 객체를 새로 생성하지는 않으므로 효율적인 것이다.

 

아래에서 보면 100개의 아이템이 있다면 100개의 아이템을 생성해야하는 리스트뷰와 달리 리사이클러 뷰는 13개의 아이템으로 객체를 재활용하며 계속 사용 할 수 있는 장점이 있다. 

 

 

리사이클러뷰 프로그래밍 순서

리사이클러뷰를 어떤 용도로 사용할지, 어떤 형태로 표시할지에 따라 그 구현 과정의 세부적인 내용와 코드의 복잡도는 달라지겠지만, 일반적으로 아래와 같은 절차를 통해 리사이클러뷰를 사용할 수 있다. (참고로, 아래 순서는 리사이클러뷰 사용 과정을 쉽게 설명하기 위해 정리한 것 일 뿐, 반드시 그대로 따를 필요는 없습니다. 각 단계는 경우에 따라 변경 또는 생략해도 무방하다.)

 

 

1. 리사이클러 뷰를 생성할 위치 선택

 

가장 먼저 어느 뷰에 리사이클러 뷰를 올릴지 선택하고 해당 뷰의 xml에 RecyclerView를 올려준다.

여기서는 MainActivity에 리사이클러 뷰를 올리고자 한다.

 

<activity_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"
    tools:context=".MainActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/recycler_view"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

2. 리사이클러 뷰에 들어가는 아이템 생성

 

리사이클러 뷰 속에 들어가는 아이템을 어떻게 만들지 xml 정의를한다.

여기서는 imageView 아래에 main, sub textvView 두개를 넣어두었다.

 

 

<recycler_item.xml>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="15dp"
    android:layout_marginEnd="15dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/item_image"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <TextView
        android:id="@+id/icon_main_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        app:layout_constraintTop_toBottomOf="@id/item_image"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <TextView
        android:id="@+id/icon_sub_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15sp"
        app:layout_constraintTop_toBottomOf="@id/icon_main_text"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

3. 아이템 클래스 정의

 

추후 아이템에 어떤 값이 들어갈지 아이템의 객체를 만들어준다.  

package com.example.recyclerviewtest;

import android.graphics.drawable.Drawable;

public class RecyclerViewItem {
    private Drawable iconDrawable;
    private String mainTitle;
    private String subTitle;

    public void setIcon(Drawable icon) {
        iconDrawable = icon;
    }

    public void setMainTitle(String mainTitle) {
        this.mainTitle = mainTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }

    public Drawable getIconDrawable() {
        return iconDrawable;
    }

    public String getMainTitle() {
        return mainTitle;
    }

    public String getSubTitle() {
        return subTitle;
    }
}

 

 

4. 어댑터 및 뷰홀더 상속 / 구현

 

어댑터 및 뷰홀더를 상속하여 구현한다.

이때 Inner class로 뷰홀더를 먼저 만들어 준 후 어댑터를 이어 구현해준다.

 

 

* ViewHolder란?

 

스크롤을 밑으로 내릴 때, 리사이클러 뷰는 가장 위에있던 뷰를 가장 아래쪽으로 이동시켜 재활용 한다. 즉, 생성과 동시에 정의된 뷰 객체들만 계속해서 위에서 아래로 이동하면서 재사용 되는 것이다.

 

따라서 생성된 뷰 객체들에서만 데이터가 수정이 되면 되는 것인데(가장 위의 것이 가장 아래로 갈때 데이터가 수정)

이 말이 즉슨 뷰 객체를 재사용한다는 의미이고 재사용 되는 뷰 객체의 데이터는 계속해서 바뀐다는 것을 의미한다.

따라서 맨 처음 뷰 객체를 기억하고 있을(홀딩) 객체가 필요한데 이 것이 ViewHolder이다. 

package com.example.recyclerviewtest;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Log;
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.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
    private ArrayList<RecyclerViewItem> mData = null;

    public RecyclerViewAdapter(ArrayList<RecyclerViewItem> data) {
        mData = data;
    }

    // onCreateViewHolder : 아이템 뷰를 위한 뷰홀더 객체를 생성하여 리턴
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View view = inflater.inflate(R.layout.recycler_item, parent, false);
        RecyclerViewAdapter.ViewHolder vh = new RecyclerViewAdapter.ViewHolder(view);

        return vh;
    }

    // onBindViewHolder : position에 해당하는 데이터를 뷰홀더의 아이템뷰에 표시
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        RecyclerViewItem item = mData.get(position);

        holder.imageView.setBackground(item.getIconDrawable());
        holder.mainText.setText(item.getMainTitle());
        holder.subText.setText(item.getSubTitle());
    }

    // getItemCount : 전체 데이터의 개수를 리턴
    @Override
    public int getItemCount() {
        return mData.size();
    }

    // 아이템 뷰를 저장하는 뷰홀더 클래스
    public class ViewHolder extends RecyclerView.ViewHolder {
        ImageView imageView;
        TextView mainText;
        TextView subText;

        ViewHolder(View itemView) {
            super(itemView);

            // 뷰 객체에 대한 참조
            imageView = itemView.findViewById(R.id.item_image);
            mainText = itemView.findViewById(R.id.icon_main_text);
            subText = itemView.findViewById(R.id.icon_sub_text);
        }
    }
}

 

 

5. 어댑터 생성 및 데이터 추가

 

리사이클러 뷰에 이용될 어댑터를 생성하여 현재 리사이클러 뷰에 지정해준다.

(Adapter는 여러 아이템을 리사이클러 뷰에 바인딩 시켜주기 위한 사전 작업이 이루어 지는 객체이다.)

이때 리사이클러 뷰에 LayoutManager을 설정해야 리사이클러 뷰의 방향을 결정할 수 있다.

현재는 가로로 슬라이딩 되도록 하기 위해 HORIZONTAL을 이용하였다.

 

마지막으로 이전에 만들어둔 Recycler item 객체인 RecyclerViewItem에 addItem을 하여 mList에 추가해준다.

그 후 어댑터의 notifyDataSetChanged를 호출해주면 추가된 list를 파악하여 아이템을 표시하게 된다.

package com.example.recyclerviewtest;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    RecyclerView mRecyclerView = null;
    RecyclerViewAdapter mAdapter = null;
    ArrayList<RecyclerViewItem> mList;

    private Drawable mImageDrawable;
    private String mMainText;
    private String mSubText;

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

        mRecyclerView = findViewById(R.id.recycler_view);
        mList = new ArrayList<>();

        mAdapter = new RecyclerViewAdapter(mList);
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));

        mImageDrawable = ResourcesCompat.getDrawable(getResources(), R.drawable.crocus, null);
        mMainText = "Crocus";
        mSubText = "www.crocus.co.kr";

        addItem(mImageDrawable, mMainText + " - 1",  mSubText);
        addItem(mImageDrawable, mMainText + " - 2",  mSubText);
        addItem(mImageDrawable, mMainText + " - 3",  mSubText);
        addItem(mImageDrawable, mMainText + " - 4",  mSubText);
        addItem(mImageDrawable, mMainText + " - 5",  mSubText);

        mAdapter.notifyDataSetChanged();
    }

    private void addItem(Drawable icon, String mainText, String subText) {
        RecyclerViewItem item = new RecyclerViewItem();

        item.setIcon(icon);
        item.setMainTitle(mainText);
        item.setSubTitle(subText);

        mList.add(item);
    }
}

 

 

 

 

 

 

출처

recipes4dev.tistory.com/167

wooooooak.github.io/android/2019/03/28/recycler_view/

 

반응형