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

[Android][Java] RecyclerView에 Loading Progress Bar 아이템 추가하기

by teamnova 2023. 4. 23.

RecylclerView에 기존 데이터를 담은 아이템과 함께 마지막 아이템을 스크롤 할 때 보여줄 Loading Progress Bar 아이템을 추가해 보겠습니다.

 

먼저 MainActivity에서 보여줄 레이아웃과 2가지 RecyclerView 아이템 레이아웃을 만듭니다

 

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"
        android:layout_marginTop="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="28dp"
        android:text="목록"
        android:textSize="24sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

recy_item.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/number"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="TextView"
        android:textColor="@color/black"
        android:textSize="20sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

progress_item.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">

    <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>

 

RecyclerView에 사용할 Adapter 클래스를 만들어 줍니다

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

    // 두가지 아이템을 구분하기 위해 ViewType 변수 선언
    final int VIEW_TYPE_POSITIVE = 1;
    final int VIEW_TYPE_NEGATIVE = 0;

    // 표시할 양수 ArrayList
    ArrayList<Integer> positiveNumberList;

    // Adapter 생성자를 사용해 ArrayList 초기화
    public Adapter(ArrayList<Integer> positiveNumbers){
        positiveNumberList = positiveNumbers;
    }

    
    // ViewHolder 셍성 메소드
    @NonNull
    @Override
    public Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = null;

        // ViewType을 확인한 뒤 그에 맞는 ViewHolder 생성
        if (viewType == VIEW_TYPE_POSITIVE) {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recy_item, parent, false);

            // 양수를 표시할 ViewHolder
            return new PositiveNumberViewHolder(view);
        } else {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.progress_item, parent, false);

            // Loading Progress bar를 표시할 ViewHolder
            return new ProgressViewHolder(view);
        }
    }

    // ViewHolder에 데이터 바인딩 메소드
    @Override
    public void onBindViewHolder(@NonNull Adapter.ViewHolder holder, int position) {
        // 데이터를 바인딩할 ViewHolder가 양수를 표시하는 ViewHolder일 경우
        if (holder instanceof PositiveNumberViewHolder){
            // 현재 ArrayList에 담겨있는 양수 설정
            ((PositiveNumberViewHolder) holder).getPositiveNumber().setText(String.valueOf(positiveNumberList.get(position)));
        }
    }

    // ViewType을 결정하는 메소드
    @Override
    public int getItemViewType(int position) {

        if (positiveNumberList.get(position) != -1)
            // ArrayList에서 position에 위치한 정수가 -1이 아닐 경우 ViewType을 VIEW_TYPE_POSITIVE로 설정
            return VIEW_TYPE_POSITIVE;
        else
            // ArrayList에서 position에 위치한 정수가 -1이 아닐 경우 ViewType을 VIEW_TYPE_NEGATIVE로 설정
            return VIEW_TYPE_NEGATIVE;
    }

    // 현재 목록에 표시해야할 아이템의 개수를 반환
    @Override
    public int getItemCount() {
        return positiveNumberList.size();
    }

    // Loading Progress bar를 보여주기 위해 임의로 -1을 ArrayList 마지막에 추가하는 메소드
    public void addProgress() {
        positiveNumberList.add(-1);
        notifyItemInserted(positiveNumberList.size() - 1);
    }
    // 임의로 보여준 Loading Progress bar를 제거하기 위해 ArrayList에서 마지막 정수를 제거하는 메소드
    public void removeProgress() {
        positiveNumberList.remove(positiveNumberList.size() - 1);
        notifyItemRemoved(positiveNumberList.size());
    }

    // 목록에 보여줄 데이터를 추가하는 메소드
    public void addNumbers(ArrayList<Integer> positiveNumbers){
        for(int i = 0; i < positiveNumbers.size(); i++){
            positiveNumberList.add(positiveNumbers.get(i));
            notifyItemInserted(positiveNumberList.size()-1);
        }
    }

    // 부모 뷰홀더를 상속한 Loading Progress bar 뷰홀더
    public class ProgressViewHolder extends ViewHolder{
        public ProgressViewHolder(View itemView){
            super(itemView);
        }
    }

    // 부모 뷰홀더를 상속한 양수 뷰홀더
    public class PositiveNumberViewHolder extends ViewHolder{
        // 양수를 표시할 TextView 변수
        TextView positiveNumber;
        public PositiveNumberViewHolder(View itemView){
            super(itemView);
            
            positiveNumber = (TextView) itemView.findViewById(R.id.number);
        }

        // TextView 반환 메소드
        public TextView getPositiveNumber(){
            return positiveNumber;
        }
    }

    // 2개의 뷰홀더를 사용하기 위해 부모 뷰홀더를 만듬
    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
        }
    }
}

 

MainActivity 클래스에 onCreate 안에 코드를 작성해줍니다

public class MainActivity extends AppCompatActivity {

    // 마지막으로 추가한 숫자를 저장할 변수
    int lastNum;

    // 연속 클릭 방지 시간 측정용 변수
    private Long mLastClickTime = 0L;

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

        // 변수 초기화
        lastNum = 0;

        // 초기 양수 목록을 만들기 위해 ArrayList 생성
        ArrayList<Integer> positiveNumberList = new ArrayList<>();
        // ArrayList에 양수 추가
        for(int i = 0; i < 20; i++){
            lastNum++;
            positiveNumberList.add(lastNum);
        }

        // 리사이클러뷰 초기화
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recy);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(MainActivity.this);
        recyclerView.setLayoutManager(linearLayoutManager);
        Adapter adapter = new Adapter(positiveNumberList);
        recyclerView.setAdapter(adapter);

        // 리사이클러뷰를 스크롤에 반응하는 리스너 설정
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            // ScrollState가 변경되었을 때 호출되는 메소드 (SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING)
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                //  최상단 또는 최하단에 있는 상태일 경우 실행
                if(newState == RecyclerView.SCROLL_STATE_SETTLING){
                    // 최하단일 경우 실행
                    if(recyclerView.canScrollVertically(1)){
                        // 데이터를 추가하는 코드를 실행한지 1초가 지났으면 실행 (Loading Progress Bar를 보여주기 위해 코드 실행을 멈추는 Thread.sleep를 사용해서 임의로 추가)
                        if(SystemClock.elapsedRealtime() - mLastClickTime > 1000){
                            mLastClickTime = SystemClock.elapsedRealtime(); // 코드 실행시 현재 시간 저장

                            // 목록에 Loading progress bar 추가
                            adapter.addProgress();

                            // Thread.sleep을 사용하기 Thread 생성 후 시작
                            new Thread(){
                                @Override
                                public void run() {
                                    super.run();

                                    try {
                                        // 0.75초간 해당 스레드 정지
                                        Thread.sleep(750);
                                    } catch (InterruptedException e) {
                                        throw new RuntimeException(e);
                                    }

                                    // 목록에 추가할 양수 ArrayList 생성
                                    ArrayList numbers = new ArrayList();
                                    for(int i = 0; i < 20; i++){
                                        lastNum++;
                                        numbers.add(lastNum);
                                    }

                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            // 양수 데이터를 추가하기 전 목록에 Loading progress bar 제거
                                            adapter.removeProgress();

                                            // 양수 데이터 추가
                                            adapter.addNumbers(numbers);
                                        }
                                    });
                                }
                            }.start();
                        }
                    }
                }
            }
        });


    }
}

 

이렇게 만들면 RecyclerView의 최하단 아이템까지 드래그할 경우 Loading Progress Bar를 보여주고 아이템을 추가해줍니다.