Computer >> 컴퓨터 >  >> 프로그래밍 >> Android

안드로이드 RecyclerView 어댑터 데이터 업데이트 방법 완벽 정리

안드로이드 RecyclerView란?

본격적인 예제에 들어가기 전에, 안드로이드에서 RecyclerView(리사이클러뷰)가 무엇인지 먼저 알아보겠습니다. RecyclerView는 ListView의 더 발전된 형태로, ViewHolder 디자인 패턴을 기반으로 동작합니다. RecyclerView를 활용하면 그리드 형태나 리스트 형태의 아이템을 효율적으로 화면에 표시할 수 있습니다.

이번 예제에서는 학생 이름과 나이를 표시하는 학생 정보 앱을 만들면서, RecyclerView 어댑터의 데이터를 동적으로 업데이트하는 방법을 단계별로 살펴보겠습니다.


1단계: 새 프로젝트 생성

Android Studio에서 File → New Project를 선택하고, 필요한 정보를 모두 입력하여 새 프로젝트를 생성합니다.

2단계: 라이브러리 의존성 추가

build.gradle 파일을 열고 RecyclerView와 CardView 라이브러리 의존성을 추가합니다.

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.example.andy.tutorialspoint"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:design:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:cardview-v7:28.0.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

3단계: 메인 레이아웃 구성

res/layout/activity_main.xml 파일에 아래 코드를 추가합니다.

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout
    xmlns:android = "https://schemas.android.com/apk/res/android"
    xmlns:tools = "https://schemas.android.com/tools"
    xmlns:app = "https://schemas.android.com/apk/res-auto"
    android:layout_width = "match_parent"
    android:layout_height = "match_parent"
    app:layout_behavior = "@string/appbar_scrolling_view_behavior"
    tools:showIn = "@layout/activity_main"
    tools:context = ".MainActivity">
    <android.support.v7.widget.RecyclerView
        android:id = "@+id/recycler_view"
        android:layout_width = "match_parent"
        android:layout_height = "wrap_content"
        android:layout_marginBottom = "50dp"
        android:scrollbars = "vertical" />
    <LinearLayout
        android:layout_width = "match_parent"
        android:layout_below = "@+id/recycler_view"
        android:layout_marginTop = "-50dp"
        android:layout_alignParentBottom = "true"
        android:layout_height = "wrap_content">
        <Button
            android:id = "@+id/add"
            android:layout_width = "wrap_content"
            android:layout_height = "wrap_content"
            android:text = "add item"/>
        <Button
            android:id = "@+id/remove"
            android:layout_width = "wrap_content"
            android:text = "remove item"
            android:layout_height = "wrap_content" />
    </LinearLayout>
</RelativeLayout>

위 코드에서는 부모 레이아웃인 RelativeLayout 안에 RecyclerView를 배치하고, 데이터를 추가(add)하거나 삭제(remove)하는 두 개의 버튼을 배치했습니다. 추가 버튼은 RecyclerView 어댑터에 데이터를 넣고, 삭제 버튼은 RecyclerView에서 데이터를 제거하는 역할을 합니다.

4단계: MainActivity 구현

src/MainActivity.java 파일에 다음 코드를 작성합니다.

package com.example.andy.tutorialspoint;

import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;


public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private StudentAdapter studentAdapter;
    private List studentDataList = new ArrayList<>();
    @TargetApi(Build.VERSION_CODES.O)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button add = findViewById(R.id.add);
        Button remove = findViewById(R.id.remove);
        recyclerView = findViewById(R.id.recycler_view);
        studentAdapter = new StudentAdapter(studentDataList,MainActivity.this);
        RecyclerView.LayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        recyclerView.setAdapter(studentAdapter);
        StudentDataPrepare();
        remove.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(studentDataList.size()>0) {
                    studentDataList.remove(studentDataList.size() - 1);
                    studentAdapter.notifyDataSetChanged();
                    Toast.makeText(MainActivity.this, String.valueOf(studentDataList.size()), Toast.LENGTH_LONG).show();
                }
            }
        });
        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(studentDataList.size()> = 0) {
                    studentData data = new studentData("raghu ram", 25);
                    studentDataList.add(studentDataList.size(), data);
                    studentAdapter.notifyDataSetChanged();
                    Toast.makeText(MainActivity.this, String.valueOf(studentDataList.size()), Toast.LENGTH_LONG).show();
                }
            }
        });
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    private void StudentDataPrepare() {
        studentData data = new studentData("sai", 25);
        studentDataList.add(data);
        data = new studentData("sai raj", 25);
        studentDataList.add(data);
        data = new studentData("raghu", 20);
        studentDataList.add(data);
        data = new studentData("raj", 28);
        studentDataList.add(data);
        data = new studentData("amar", 15);
        studentDataList.add(data);
        data = new studentData("bapu", 19);
        studentDataList.add(data);
        data = new studentData("chandra", 52);
        studentDataList.add(data);
        data = new studentData("deraj", 30);
        studentDataList.add(data);
        data = new studentData("eshanth", 28);
        studentDataList.add(data);
        Collections.sort(studentDataList, new Comparator() {
            @Override
            public int compare(studentData o1, studentData o2) {
                return o1.name.compareTo(o2.name);
            }
        });
    }
}

위 코드에서는 RecyclerView와 StudentAdapter를 연결했습니다. 어댑터에는 학생 이름과 나이가 담긴 ArrayList 형태의 studentDataList를 전달합니다. 그리고 추가(add)와 삭제(remove) 두 개의 버튼 리스너를 등록했습니다.

데이터 추가 로직

추가 버튼을 누르면 아래 코드처럼 ArrayList에 새 아이템을 삽입할 수 있습니다.

if(studentDataList.size()> = 0) {
    studentData data = new studentData("raghu ram", 25);
    studentDataList.add(studentDataList.size(), data);
    studentAdapter.notifyDataSetChanged();
    Toast.makeText(MainActivity.this, String.valueOf(studentDataList.size()), Toast.LENGTH_LONG).show();
}

위 코드는 ArrayList의 크기가 0 이상인지 검증한 후, 리스트 끝에 새 데이터를 추가합니다. 여기서 핵심은 notifyDataSetChanged() 메서드입니다. 이 메서드는 어댑터에게 "데이터셋이 변경되었다"고 알려주어, 어댑터가 내부적으로 화면을 갱신하도록 만듭니다.

데이터 삭제 로직

ArrayList에서 데이터를 제거할 때는 remove() 메서드를 사용합니다.

if(studentDataList.size()>0) {
    studentDataList.remove(studentDataList.size() - 1);
    studentAdapter.notifyDataSetChanged();
    Toast.makeText(MainActivity.this, String.valueOf(studentDataList.size()), Toast.LENGTH_LONG).show();
}

위 코드는 size - 1 인덱스를 기준으로 데이터를 삭제합니다. 즉, 리스트 맨 아래(마지막) 항목부터 제거됩니다. 데이터가 삭제된 후에는 notifyDataSetChanged()를 호출해 어댑터에 변경 사항을 알립니다.

5단계: StudentAdapter 구현

src/StudentAdapter.java 파일의 내용은 다음과 같습니다.

package com.example.andy.tutorialspoint;

import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
import java.util.Random;

class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.MyViewHolder> {
    List<studentData> studentDataList;
    public StudentAdapter(List<studentData> studentDataList) {
        this.studentDataList = studentDataList;
    }
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View itemView = LayoutInflater.from(viewGroup.getContext())
        .inflate(R.layout.student_list_row, viewGroup, false);
        return new MyViewHolder(itemView);
    }
    @Override
    public void onBindViewHolder(MyViewHolder viewHolder, int i) {
        studentData data = studentDataList.get(i);
        Random rnd = new Random();
        int currentColor = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
        viewHolder.parent.setBackgroundColor(currentColor);
        viewHolder.name.setText(data.name);
        viewHolder.age.setText(String.valueOf(data.age));
    }
    @Override
    public int getItemCount() {
        return studentDataList.size();
    }
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView name,age;
        LinearLayout parent;
        public MyViewHolder(View itemView) {
            super(itemView);
            parent = itemView.findViewById(R.id.parent);
            name = itemView.findViewById(R.id.name);
            age = itemView.findViewById(R.id.age);
        }
    }
}  

어댑터 클래스에는 다음 네 가지 핵심 요소가 포함되어 있습니다.

  • onCreateViewHolder(): ViewHolder를 생성하고 해당 뷰를 반환하는 메서드입니다.

  • onBindViewHolder(): 생성된 ViewHolder에 실제 데이터를 바인딩(연결)하는 메서드입니다.

  • getItemCount(): 리스트의 크기를 반환하는 메서드입니다.

  • MyViewHolder 클래스: RecyclerView.ViewHolder를 상속받는 내부 ViewHolder 클래스입니다.

RecyclerView 아이템에 랜덤한 배경색을 적용하기 위해, 안드로이드에 미리 정의된 Random 클래스로 임의 색상을 생성하고 이를 아이템 뷰의 부모 레이아웃에 적용했습니다.

Random rnd = new Random();
int currentColor = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
viewHolder.parent.setBackgroundColor(currentColor);

6단계: 리스트 아이템 레이아웃 작성

res/layout/student_list_row.xml 파일의 내용은 다음과 같습니다.

<?xml version = "1.0" encoding = "utf-8"?>
<android.support.v7.widget.CardView xmlns:android = "https://schemas.android.com/apk/res/android"
    xmlns:card_view = "https://schemas.android.com/apk/res-auto"
    android:layout_width = "match_parent"
    card_view:cardCornerRadius = "4dp"
    android:id = "@+id/card_view"
    android:layout_margin = "10dp"
    android:layout_height = "200dp">
    <LinearLayout
        android:id = "@+id/parent"
        android:layout_gravity = "center"
        android:layout_width = "match_parent"
        android:orientation = "vertical"
        android:gravity = "center"
        android:layout_height = "match_parent">
    <TextView
        android:id = "@+id/name"
        android:layout_width = "wrap_content"
        android:gravity = "center"
        android:textSize = "25sp"
        android:textColor = "#FFF"
        android:layout_height = "wrap_content" />
    <TextView
        android:id = "@+id/age"
        android:layout_width = "wrap_content"
        android:gravity = "center"
        android:textSize = "25sp"
        android:textColor = "#FFF"
        android:layout_height = "wrap_content" />
    </LinearLayout>
</android.support.v7.widget.CardView>

위 리스트 아이템 뷰에서는 CardView 안에 이름(name)과 나이(age)를 표시하는 두 개의 TextView를 배치했습니다. CardView는 기본적으로 모서리 둥글기(cornerRadius)와 그림자(shadow) 속성을 제공하므로, 여기서는 cardCornerRadius 속성을 활용했습니다.

7단계: 데이터 모델 클래스 작성

src/studentData.java 파일의 내용은 다음과 같습니다.

package com.example.andy.tutorialspoint;

class studentData {
    String name;
    int age;
    public studentData(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

위 코드는 학생 데이터 객체를 정의한 것입니다. 이제 애플리케이션을 실행해 보겠습니다. 실제 안드로이드 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 열고 툴바의 Run 아이콘을 클릭하세요. 실행할 모바일 기기를 선택하면, 기기 화면에 아래와 같은 결과가 표시됩니다.

안드로이드 RecyclerView 어댑터 데이터 업데이트 방법 완벽 정리

처음에는 마지막 항목이 나이 25세의 "sai raj"입니다. 여기서 두 개의 항목을 추가하면 아래와 같이 표시됩니다.

안드로이드 RecyclerView 어댑터 데이터 업데이트 방법 완벽 정리

이번에는 모든 항목을 삭제하면 출력 결과는 다음과 같습니다.

안드로이드 RecyclerView 어댑터 데이터 업데이트 방법 완벽 정리

마무리

이번 튜토리얼에서는 RecyclerView 어댑터의 데이터를 동적으로 추가하고 삭제하는 방법을 알아보았습니다. 핵심은 데이터 리스트를 변경한 후 반드시 notifyDataSetChanged()를 호출하여 어댑터에 변경 사항을 알리는 것입니다. 참고로, 성능 최적화가 필요한 경우 notifyDataSetChanged() 대신 notifyItemInserted(), notifyItemRemoved() 같은 세분화된 갱신 메서드를 사용하는 것이 좋습니다.