728x90
오늘은 리사이클러뷰에서 페이징 처리를 할 때 데이터를 불러오는 동안 로딩아이템을 추가하는 예제를 만들어 보겠습니다.
이 예제를 통해서 사용자가 이해하기 쉽고 이탈률을 낮출 수 있는 UI를 만들 수 있습니다.
먼저 리사이클러뷰 만드는 법에 대한 설명은 다음 글을 참고해 주세요.
https://stickode.tistory.com/40
그 다음 예제 동작 순서에 대해 간단하게 설명드리겠습니다.
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>
이렇게 작성해주시면 아래와 같이 로딩아이템을 데이터 요청 후 응답 받는 동안 사용자에게 보여줄 수 있습니다!
'안드로이드 자바' 카테고리의 다른 글
[Android][Java] Retrofit 라이브러리를 사용해서 서버에 파일 업로드하기(클라이언트) (0) | 2023.10.09 |
---|---|
[Android][java] 바인딩 서비스와 플래그 (0) | 2023.10.07 |
[Android][java] 서비스 onStartCommand()와 플래그 (0) | 2023.09.26 |
[Android][Java] 비트맵 합치기 PorterDuff.Mode (0) | 2023.09.23 |
[Android][Java] 리사이클러뷰 notifyItemChanged 활용하여 리사이클러뷰 아이템 특정 요소 수정하기 (좋아요 기능) (2) | 2023.09.18 |