본문 바로가기
안드로이드 자바

[Android][Java] 리사이클러뷰 로딩아이템 만들기

by teamnova 2023. 9. 28.
728x90

오늘은 리사이클러뷰에서 페이징 처리를 할 때 데이터를 불러오는 동안 로딩아이템을 추가하는 예제를 만들어 보겠습니다.

이 예제를 통해서 사용자가 이해하기 쉽고 이탈률을 낮출 수 있는  UI를 만들 수 있습니다.

 

먼저 리사이클러뷰 만드는 법에 대한 설명은 다음 글을 참고해 주세요.

https://stickode.tistory.com/40

 

[Java][Android] 리사이클러뷰 만들기

이번 포스팅에서는 리사이클러뷰를 만드는 방법을 알아보겠습니다. 리사이클러뷰란 ? 기존의 ListView을 보완하기위해 , ViewHolder를 통하여 뷰를 재사용하며 성능적인 면을 개선하고 , 커스터마이

stickode.tistory.com

 

그 다음 예제 동작 순서에 대해 간단하게 설명드리겠습니다.

1. 사용자가 스크롤을 바닥 끝까지 내린다.

2-1. (이미 데이터를 요청한 상태일 경우) 이후 3~5번을 진행하지 않는다.

2-2. (이미 데이터를 요청한 상태가 아닐 경우) 로딩 아이템을 리사이클러뷰 아이템 리스트 마지막에 추가한다.

3. 데이터를 요청한다.

4. 요청된 데이터가 준비됐을 때 로딩 아이템을 제거한다.

5. 확보한 데이터를 리사이클러뷰 아이템 리스트에 추가한다.

 

다음은 Java 코드와 XML 코드입니다.

MainActivity.java

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

import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

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

        // 숫자 목록을 담을 리사이클러뷰 초기화
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recy);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
        recyclerView.setLayoutManager(linearLayoutManager);

        // 초기 입력 데이터 준비
        ArrayList<Integer> newList = new ArrayList<>();
        for(int i = 0; i < 30; i++){
            newList.add(i);
        }

        // 리사이클러뷰 어댑터 초기화 및 리사이클러뷰에 어댑터 설정
        Adapter adapter = new Adapter(newList);
        recyclerView.setAdapter(adapter);

        // 리사이클러뷰 스크롤리스너 설정(스크롤 상태가 변경되었을 때 호출)
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                if(adapter.getLastItem() != -1){ // 현재 리사이클러뷰 아이템 리스트에 로딩 아이템이 있는지 확인, 없다면 내부 코드 실행
                    if(!recyclerView.canScrollVertically(1)){ // 현재 리사이클러뷰의 최하단이여서 더 이상 스크롤 할 수 없을 경우 내부 코드 실행

                        // 리사이클러뷰 아이템 리스트에 로딩 아이템 추가
                        adapter.addLoadingData();

                        // 눈에 보이는 시연을 위해 데이터 요청 후 응답 시간을 임의로 설정하는 스레드
                        new Thread(){
                            @Override
                            public void run() {

                                // 데이터 요청 및 응답처럼 시간을 사용하기 위해 스레드를 0.5초 일시정지
                                try {
                                    Thread.sleep(500);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }

                                // 추가할 데이터 생성
                                int cnt = adapter.getItemCount();
                                ArrayList<Integer> addList =  new ArrayList<>();
                                for(int i = 0; i < 30; i++){
                                    addList.add(cnt+i);
                                }

                                // UI변경을 위해 데이터 업데이트는 메인스레드에서 실행
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        adapter.addItems(addList);
                                    }
                                });
                            }
                        }.start();
                    }
                }

            }
        });
    }
}

 

Adapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;

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

import java.util.ArrayList;

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

    ArrayList<Integer> numberlist;

    public Adapter(ArrayList<Integer> newList){
        numberlist = newList;
    }

    @NonNull
    @Override
    public Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemview, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull Adapter.ViewHolder holder, int position) {
        if(numberlist.get(position) == -1){ // 해당 아이템이 로딩 아이템일 경우 숫자 표시 view 제거 및 로딩 view 표시
            holder.getTextView().setVisibility(View.GONE);
            holder.getProgressBar().setVisibility(View.VISIBLE);
        }else{ // 해당 아이템이 숫자 아이템일 경우 숫자 view 표시 및 로딩 view 제거
            holder.getTextView().setVisibility(View.VISIBLE);
            holder.getProgressBar().setVisibility(View.GONE);

            holder.getTextView().setText(String.valueOf(numberlist.get(position)));
        }
    }

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

    // 로딩 아이템 제거 및 숫자 데이터 추가 메소드
    public void addItems(ArrayList<Integer> newList){
        // 숫자 데이터를 추가하기 전 로딩아이템 제거
        numberlist.remove(numberlist.size()-1);
        notifyItemRemoved(numberlist.size());

        // 숫자 데이터 추가
        for(int i = 0; i < newList.size(); i++){
            numberlist.add(newList.get(i));
            notifyItemInserted(numberlist.size()-1);
        }
    }

    // 로딩 아이템 추가 메소드
    public void addLoadingData(){
        numberlist.add(-1);
        notifyItemInserted(numberlist.size());
    }

    // 현재 데이터를 불러오는 중인지 확인하기 위해 마지막 아이템의 값을 반환 (-1일 경우 로딩 아이템)
    public int getLastItem(){
        return numberlist.get(numberlist.size()-1);
    }


    public class ViewHolder extends RecyclerView.ViewHolder {

        ProgressBar progressBar;
        TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            progressBar = (ProgressBar) itemView.findViewById(R.id.progressBar);
            textView = (TextView) itemView.findViewById(R.id.textView);
        }

        public ProgressBar getProgressBar(){
            return progressBar;
        }

        public TextView getTextView(){
            return textView;
        }
    }
}

 

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:id="@+id/recy"
        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" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

itemview.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="wrap_content">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:text="num"
        android:textColor="@color/black"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 

이렇게 작성해주시면 아래와 같이 로딩아이템을 데이터 요청 후 응답 받는 동안 사용자에게 보여줄 수 있습니다!