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

[Java][Android] 리사이클러뷰에 페이징 적용하기

by teamnova 2021. 4. 8.

이번 포스팅에선 리사이클러뷰에 간단하게 페이징을 적용해 보겠습니다.

 

 

먼저 의존성 문구를 앱 수준 gradle 파일에 추가해줍니다.

레트로핏으로 네트워크 통신을 한 다음, 이미지를 받아와 리사이클러뷰로 보여줄 것이기 때문에 아래와 같은 의존성 문구들을 넣어주면 됩니다.

implementation 'com.google.android.material:material:1.1.0'
implementation 'com.squareup.retrofit2:retrofit:2.6.4'
implementation 'com.squareup.retrofit2:converter-scalars:2.6.4'
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

 

그리고 레트로핏을 쓸 것이기 때문에 매니페스트에 인터넷 태그는 꼭 넣어줘야 합니다.

<uses-permission android:name="android.permission.INTERNET"/>

 

이제 리사이클러뷰에 붙일 어댑터를 만들어 보겠습니다.

먼저 어댑터에서 사용할 아이템 xml입니다. 이름은 list_row_main.xml입니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_margin="4dp"
    app:cardCornerRadius="4dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/image_view"
            android:layout_width="150dp"
            android:layout_height="80dp"
            android:scaleType="centerCrop"/>

        <TextView
            android:id="@+id/text_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:gravity="center"/>

    </LinearLayout>

</androidx.cardview.widget.CardView>

 

다음으로 모델 클래스 코드입니다.

public class MainData
{
    private String image, name;

    public String getImage()
    {
        return image;
    }

    public void setImage(String image)
    {
        this.image = image;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}

 

다음으로 어댑터 코드입니다.

import android.app.Activity;
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.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;

import java.util.ArrayList;

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder>
{
    private ArrayList<MainData> dataArrayList;
    private Activity activity;

    public MainAdapter(Activity activity, ArrayList<MainData> dataArrayList)
    {
        this.activity = activity;
        this.dataArrayList = dataArrayList;
    }

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

    @Override
    public void onBindViewHolder(@NonNull MainAdapter.ViewHolder holder, int position)
    {
        MainData data = dataArrayList.get(position);

        Glide.with(activity)
                .load(data.getImage())
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .into(holder.imageView);

        holder.textView.setText(data.getName());
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder
    {
        ImageView imageView;
        TextView textView;

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

            imageView = view.findViewById(R.id.image_view);
            textView = view.findViewById(R.id.text_view);
        }
    }
}

 

다음은 레트로핏을 사용할 때 필요한 인터페이스 클래스입니다.

GET 메서드를 쓰기 때문에 아래와 같이 만들었습니다.

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface MainInterface
{
    @GET("v2/list")
    Call<String> string_call(
            @Query("page") int page,
            @Query("limit") int limit
    );
}

 

이제 메인 액티비티의 xml부터 만들어 보겠습니다.

<androidx.core.widget.NestedScrollView 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"
    android:id="@+id/scroll_view"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="4dp">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:nestedScrollingEnabled="false"
            tools:listitem="@layout/list_row_main"/>

        <ProgressBar
            android:id="@+id/progress_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </LinearLayout>

</androidx.core.widget.NestedScrollView>

 

아래에서 위로 스크롤 할 때마다 모든 데이터를 다 보여주면 제일 하단 부분에 둥글게 돌아가는 프로그레스 바를 보여줄 겁니다.

이 프로그레스 바는 다음에 보여줄 데이터들을 모두 가져와 리사이클러뷰에 들어가게 되면 사용자의 눈에 보이지 않게 됩니다.

더 자세한 내용은 아래 자바 코드를 확인해 주세요.

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

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.scalars.ScalarsConverterFactory;

public class MainActivity extends AppCompatActivity
{
    NestedScrollView nestedScrollView;
    RecyclerView recyclerView;
    ProgressBar progressBar;

    ArrayList<MainData> dataArrayList = new ArrayList<>();
    MainAdapter adapter;

    // 1페이지에 10개씩 데이터를 불러온다
    int page = 1, limit = 10;

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

        nestedScrollView = findViewById(R.id.scroll_view);
        recyclerView = findViewById(R.id.recycler_view);
        progressBar = findViewById(R.id.progress_bar);

        adapter = new MainAdapter(MainActivity.this, dataArrayList);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        getData(page, limit);

        nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener()
        {
            @Override
            public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
            {
                if (scrollY == v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())
                {
                    page++;
                    progressBar.setVisibility(View.VISIBLE);
                    getData(page, limit);
                }
            }
        });
    }

    private void getData(int page, int limit)
    {
        // 레트로핏 초기화
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://picsum.photos/")
                .addConverterFactory(ScalarsConverterFactory.create())
                .build();

        MainInterface mainInterface = retrofit.create(MainInterface.class);
        Call<String> call = mainInterface.string_call(page, limit);
        call.enqueue(new Callback<String>()
        {
            @Override
            public void onResponse(Call<String> call, Response<String> response)
            {
                if (response.isSuccessful() && response.body() != null)
                {
                    progressBar.setVisibility(View.GONE);
                    try
                    {
                        JSONArray jsonArray = new JSONArray(response.body());
                        parseResult(jsonArray);
                    }
                    catch (JSONException e)
                    {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<String> call, Throwable t)
            {
                Log.e("에러 : ", t.getMessage());
            }
        });
    }

    private void parseResult(JSONArray jsonArray)
    {
        for (int i = 0; i < jsonArray.length(); i++)
        {
            try
            {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                MainData data = new MainData();
                data.setImage(jsonObject.getString("download_url"));
                data.setName(jsonObject.getString("author"));
                dataArrayList.add(data);
            }
            catch (JSONException e)
            {
                e.printStackTrace();
            }
            adapter = new MainAdapter(MainActivity.this, dataArrayList);
            recyclerView.setAdapter(adapter);
        }
    }

}

1페이지마다 10개씩 데이터를 불러옵니다. 이제 위 예제를 실행해 보면 아래와 같이 작동하는 걸 볼 수 있습니다.

 

 

한 페이지에 모든 데이터를 담기 힘들 때 생각해볼 수 있는 것 중 하나가 리사이클러뷰인데, 리사이클러뷰로도 표현하기엔 너무 많은 데이터들을 보여줄 경우 위와 같은 페이징 처리를 생각해보는 것도 좋을 것 같습니다.

 

 

스틱코드에서 이 포스팅에 쓰인 코드를 확인해 보시려면 아래 링크를 확인해 보세요.

stickode.com/detail.html?no=2024

 

스틱코드

 

stickode.com

Glide가 아닌 다른 이미지 라이브러리를 쓰고 싶다면 아래 링크(Picasso)를 참고해 보세요.

stickode.com/detail.html?no=1157

 

스틱코드

 

stickode.com