본문 바로가기
Android

안드로이드 스튜디오, JAVA] 리사이클러 뷰(recycler View)

by 김마리님 2020. 7. 23.

https://itstudy-mary.tistory.com/208

 

안드로이드 스튜디오,JAVA] 커스텀 리스트뷰에 이미지 넣기

https://itstudy-mary.tistory.com/207 안드로이드 스튜디오, JAVA] 커스텀 리스트뷰(ListView) 만들기 https://itstudy-mary.tistory.com/206 안드로이드 스튜디오, JAVA] 기본 리스트뷰(ListView) 만들기 기본적..

itstudy-mary.tistory.com

 

저번 포스팅까지 리스트뷰에 이미지를 넣어 커스텀까지 해봤는데, 

리스트뷰의 문제점이 하나 있다. 바로, 리스트뷰는 화면을 내리면서 뷰를 그림 - 뷰 출력의 형태를 가지기 때문에 연산이 복잡하고 따라서 자료가 많은 앱의 퍼포먼스를 떨어뜨리는 원인이 되기도 한다.

 

리사이클러뷰는 이름만 봐도 알 수 있듯이 만들어놓은 뷰를 재사용한다. 따라서 뷰를 일일히 그릴 필요가 없어 뷰를 그릴때 필요한 연산이 줄어들게 된다. 그렇기 때문에 앱의 부하가 줄어든다.

 

리사이클러뷰는 처음부터 다운받아져있지 않기 때문에 직접 디자인 xml에서 다운로드 받아야 한다.

다운을 받으면 라이브러리 의존도구에 리사이클러 뷰에 대한 의존성이 걸린다. 그럼 사용이 가능해진다.

 

위의 포스팅과 같은 것을 만들 것이다.

 

먼저, 같은 아이템 레이아웃을 쓰고, 메인 화면에 리스트뷰를 삭제하고 리사이클러 뷰를 걸어둔다.

 

activity_main. xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rc_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"/>
</LinearLayout>

 

리사이클러 뷰의 큰 특징은 리스트뷰와 다르게 가로 이동도 가능하기 때문에 스크롤의 형태를 지정해줘야 하는데, 우리는 아래에서 위로 올릴 것이므로 버티컬 속성을 부여한다.

 

리사이클러 뷰도 리스트 뷰와 마찬가지로 뷰에 데이터를 넣어야 하므로 어댑터를 요구한다.

 

먼저 데이터가 영화 이름, 사진 두 종류이므로 사용자 정의 타입을 만들자.

Movie .java

package com.mary.recyclerviewex01;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder

public class Movie {
    private String title;
    private int imgResource;
}

 

리사이클러 뷰는 리스트 뷰와 만드는 방식이 조금 다르다.

 

조심히 잘 만들어야 한다.. 아니면 순서가 꼬여 참조해야하는 변수 등이 꼬일 수 있다.

 

1. 어댑터 클래스를 만든다.

2. 내부에 다시 만들어질 뷰, 즉 뷰 홀더를 담당할 내부클래스를 만든다.

3. 내부 클래스에 RecyclerView.ViewHolder 클래스를 상속 시킨다.

4. 내부 클래스에 컨테이너를 만든다(추상 클래스를 상속하므로 필수임).

5. 외부 클래스에 RecyclerView.Adapter<어댑터.뷰홀더(=내부 클래스)>를 상속시킨다.

6. 외부 클래스에 내부 매서드를 만든다(Adapter이 추상 클래스라 필수임)

 

잘 만들었다면 어댑터 코드를 본다.

 

 

package com.mary.recyclerviewex01;

import android.util.Log;
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 java.util.ArrayList;
import java.util.List;

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MyViewHolder>{
    private static final String TAG = "MovieAdapter";

    //리스트는 무조건 데이터를 필요로함
    private List<Movie> items=new ArrayList<>();

    public void addItem(Movie movie){
        items.add(movie);
    }

    //껍데기만 만듬. 1번 실행
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Log.d(TAG, "onCreateViewHolder: ");
        LayoutInflater inflater=LayoutInflater.from(parent.getContext());
        View view=inflater.inflate(R.layout.item,parent,false);
        return new MyViewHolder(view);
    }

    //껍데기에 데이터 바인딩. 2번 실행
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Log.d(TAG, "onBindViewHolder: "+position);
        Movie movie=items.get(position);
        holder.setItem(movie);
    }

    @Override
    public int getItemCount() {
        Log.d(TAG, "getItemCount: ");
        return items.size();
    }

    //ViewHolder : 뷰들의 책꽂이
    public static class MyViewHolder extends RecyclerView.ViewHolder {

        //규칙1
        private TextView tvTitle;
        private ImageView imageResource;


        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            //규칙2
            tvTitle=itemView.findViewById(R.id.tv_title);
            imageResource=itemView.findViewById(R.id.iv_img_resource);
        }

        //규칙3
        public void setItem(Movie movie){
            Log.d(TAG, "MyViewHolder: ");
            tvTitle.setText(movie.getTitle());
            imageResource.setImageResource(movie.getImgResource());
        }
    }
}

 

당연히 데이터를 담을 배열을 만들고, 배열에 데이터를 담는 함수를 만들어 외부에서 데이터를 받아 담을 수 있도록 한다(여기까진 필수 매서드가 아님).

 

처음 함수는 뷰홀더를 만드는 매서드로, 뷰 자체를 만드는 것이 아니라 뷰 홀더를 만드는 것이다.

그러니까, 뷰를 담을 바구니를 만드는 작업이다. .. 그래서 껍데기를 만드는 작업이란 소리다.

 

그리고 이 껍데기에 데이터들을 연결한다. 이후에 데이터를 받아 뷰에 출력하는 것은 내부 클래스에서 한다. 즉.. 데이터를 바인딩 한다는건 단순히 get(position)으로 데이터를 받아오는 것에서 끝난다.

 

내부 클래스에는 내부 컨테이너가 내가 참조하게 될 뷰를 변수로 가지고 있기 때문에, 내가 회전시킬 화면의 뷰를 컨테이너 밖에서 변수화 해서, 컨테이너 안에서 그 변수에 id 값을 넣어둔다.

이제 이 변수를 가지고 마지막으로 내부 클래스 안에서, 컨테이너 밖에서 아이템을 넣을 함수를 만든다(이 함수는 2번 바인딩 매서드로 올라가 바인딩 된 데이터와 연결된다.).

 

마지막으로 메인에서 호출만 해주면 끝난다.

 

- MainActivity. java

package com.mary.recyclerviewex01;

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

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Main_Activity";

    private RecyclerView rvMovie;


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

        rvMovie=findViewById(R.id.rc_view);

        MovieAdapter adapter=new MovieAdapter();
        adapter.addItem(new Movie("써니",R.drawable.mov01));
        adapter.addItem(new Movie("완득이",R.drawable.mov02));
        adapter.addItem(new Movie("괴물",R.drawable.mov03));
        adapter.addItem(new Movie("라디오스타",R.drawable.mov04));
        adapter.addItem(new Movie("비열한 거리",R.drawable.mov05));
        adapter.addItem(new Movie("왕의 남자",R.drawable.mov06));
        adapter.addItem(new Movie("아일랜드",R.drawable.mov07));
        adapter.addItem(new Movie("웰컴 투 동막골",R.drawable.mov08));
        adapter.addItem(new Movie("헬보이",R.drawable.mov09));
        adapter.addItem(new Movie("백 투더 퓨처",R.drawable.mov10));
        adapter.addItem(new Movie("여인의 향기",R.drawable.mov11));
        adapter.addItem(new Movie("쥬라기 공원",R.drawable.mov12));

        RecyclerView.LayoutManager layoutManager=new LinearLayoutManager(this);
        rvMovie.setLayoutManager(layoutManager);
        rvMovie.setAdapter(adapter);
    }
}

 

이 때, 리스트뷰와 또 다른 점은 레이아웃 매니저로 어느 컨텍스트에서, 어떤 방향으로 붙일건지도 설정해주어야 한다는 점이다.

 

저번 포스트처럼 디버그 로그를 확인해보자.

 

보다시피 뷰를 만드는 뷰홀더가 맨 처음과 맨 끝만 만들고 그 의외의 뷰를 만들지 않는 것을 확인할 수 있다.

(리스트뷰는 다 만든다)

반응형