코딩스토리

[Android/안드로이드] 하나의 아답타로 여러개의 RecyclerView만들기/ RecyclerView Adapter 재사용 본문

Android/유용한 기술

[Android/안드로이드] 하나의 아답타로 여러개의 RecyclerView만들기/ RecyclerView Adapter 재사용

라크라꾸 2019. 12. 22. 23:46

이번 시간에는 하나의 Adapter를 재사용하면서 여러개의 RecyclerView를 만들어 보겠습니다.

 

다음과같이 RecyclerView레이아웃을 만들 때 Adapter가 모듈로 정리되어있지 않을 경우라면 왼쪽에 있는 Adapter와 오른쪽에 있는 Adapter, 총 2개의 Adapter가 생겨날 것입니다. 규모가 작은 프로젝트의 경우 여러개 만든다고 해도 유지보수하기 수월하겠지만, 규모가 커질수록, RecyclerView를 많이 사용하게 된다면 Adapter의 양이 어마어마해져 유지보수를 하는데 어려움을 겪을 수 있습니다.

 

이번 시간에는 Adapter를 모듈화시키면서 유지보수하기 쉽고, 코드도 간결하도록 여러개의 Adapter클래스를 하나로 줄여보도록 하겠습니다.

 

상속받을 상위 클래스 생성

모듈화를 시키기 위해서 우선 RecyclerView.ViewHolder를 상속받는 MyItemView클래스와 Serializable를 상속받는 MyItem클래스를 생성해줍니다.

 

MyItem.java

  
  package com.example.recyclerviewtest;
  import java.io.Serializable;
  
  public class MyItem implements Serializable {
  }
  

 

MyItemView.java

  
  package com.example.recyclerviewtest;
  
  
  import android.view.View;
  
  import androidx.annotation.NonNull;
  import androidx.recyclerview.widget.RecyclerView;
  
  public class MyItemView extends RecyclerView.ViewHolder {
      public MyItemView(@NonNull View itemView) {
          super(itemView);
      }
  }
  

 

모듈화를 시키기 위해서는 클래스타입을 일치시켜 줘야 하기 때문에 Data부분에서는 상위 클래스로 MyItem을, ViewHolder부분에서는 상위클래스로 MyItemView로 지정해줘 타입을 일치시켜줍니다.

 

Data

DataMovie.java

  
  package com.example.recyclerviewtest;
  
  public class DataMovie extends MyItem{
      int image;
      String title;
  
      public DataMovie(int image, String title){
          this.image = image;
          this.title = title;
      }
  
      public int getImage() {
          return image;
      }
  
      public void setImage(int image) {
          this.image = image;
      }
  
      public String getTitle() {
          return title;
      }
  
      public void setTitle(String title) {
          this.title = title;
      }
  }
  

 

Enum클래스 생성

MovieCase.java

 
 package com.example.recyclerviewtest;
  
  public enum MovieCase {
      SMALL,LARGE
  }
  

 

이미지를 크게 보여줄 ViewHolder인지, 작게 보여줄 ViewHolder인지 구분하기 위해서 Enum클래스로 타입을 생성해줍니다.

 

EventListener

이전시간에 사용했던 RecyclerView접기/펼치기 기능을 사용하기 위해 리스너를 구현해줍니다.

  
  package com.example.recyclerviewtest;
  
  public interface OnViewHolderItemClickListener {
      void onViewHolderItemClick();
  }
  

 

ViewHolder 생성

ViewHolderMovieLarge.java

  
  package com.example.recyclerviewtest;
  
  import android.view.View;
  import android.widget.ImageView;
  import android.widget.TextView;
  
  import androidx.annotation.NonNull;
  
  public class ViewHolderMovieLarge extends  MyItemView {
  
      TextView tv_movie_title;
      ImageView iv_movie;
  
      DataMovie data;
  
  
      public ViewHolderMovieLarge(@NonNull View itemView) {
          super(itemView);
  
          iv_movie = itemView.findViewById(R.id.iv_movie);
          tv_movie_title = itemView.findViewById(R.id.tv_movie_title);
  
      }
  
      public void onBind(MyItem data){
          this.data = (DataMovie) data;
          tv_movie_title.setText(this.data.getTitle());
          iv_movie.setImageResource(this.data.getImage());
      }
  
  }
  

 

ViewHolderMovieSmall.java

  
  package com.example.recyclerviewtest;
  
  import android.animation.ValueAnimator;
  import android.view.View;
  import android.widget.ImageView;
  import android.widget.LinearLayout;
  import android.widget.TextView;
  
  import androidx.annotation.NonNull;
  
  public class ViewHolderMovieSmall extends  MyItemView {
  
      TextView tv_movie_title;
      ImageView iv_movie,iv_movie2;
      LinearLayout linearlayout;
  
      OnViewHolderItemClickListener onViewHolderItemClickListener;
  
      DataMovie data;
  
  
      public ViewHolderMovieSmall(@NonNull View itemView) {
          super(itemView);
  
          iv_movie = itemView.findViewById(R.id.iv_movie);
          tv_movie_title = itemView.findViewById(R.id.tv_movie_title);
          iv_movie2 = itemView.findViewById(R.id.iv_movie2);
          linearlayout = itemView.findViewById(R.id.linearlayout);
  
          linearlayout.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  onViewHolderItemClickListener.onViewHolderItemClick();
              }
          });
      }
  
      public void onBind(MyItem data, Boolean isSelect){
          this.data = (DataMovie) data;
          tv_movie_title.setText(this.data.getTitle());
          iv_movie.setImageResource(this.data.getImage());
          iv_movie2.setImageResource(this.data.getImage());
          changeVisibility(isSelect);
      }
  
      private void changeVisibility(final boolean isExpanded) {
          // ValueAnimator.ofInt(int... values)는 View가 변할 값을 지정, 인자는 int 배열
          ValueAnimator va = isExpanded ? ValueAnimator.ofInt(0, iv_movie2.getHeight()) : ValueAnimator.ofInt(iv_movie2.getHeight(), 0);
          // Animation이 실행되는 시간, n/1000초
          va.setDuration(500);
          va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
              @Override
              public void onAnimationUpdate(ValueAnimator animation) {
                  // imageView의 높이 변경
                  iv_movie2.requestLayout();
                  // imageView가 실제로 사라지게하는 부분
                  iv_movie2.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
              }
          });
          // Animation start
          va.start();
      }
  
      public void setOnViewHolderItemClickListener(OnViewHolderItemClickListener onViewHolderItemClickListener) {
          this.onViewHolderItemClickListener = onViewHolderItemClickListener;
      }
  }
  

 

Adapter 모듈화

RecyclerVierAdapter.java

  
  package com.example.recyclerviewtest;
  
  import android.util.SparseBooleanArray;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  
  import androidx.annotation.NonNull;
  import androidx.recyclerview.widget.RecyclerView;
  
  import java.util.ArrayList;
  
  public class RecyclerVierAdapter extends RecyclerView.Adapter<MyItemView> {
  
      // adapter에 들어갈 list 입니다.
      private ArrayList<MyItem> listData = new ArrayList<>();
  
      // Item의 클릭 상태를 저장할 array 객체
      private SparseBooleanArray selectedItems = new SparseBooleanArray();
      // 직전에 클릭됐던 Item의 position
      private int prePosition = -1;
  
      private MovieCase sel_type;
  
      public RecyclerVierAdapter(MovieCase sel_type){
          this.sel_type = sel_type;
      }
  
  
      @NonNull
      @Override
      public MyItemView onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  
          View view;
  
          if(sel_type == MovieCase.LARGE){
              view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie_large, parent, false);
              return new ViewHolderMovieLarge(view);
          } else if(sel_type == MovieCase.SMALL){
              view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie_small, parent, false);
              return new ViewHolderMovieSmall(view);
          }
          return null;
      }
  
      @Override
      public void onBindViewHolder(@NonNull MyItemView holder, final int position) {
          if(holder instanceof ViewHolderMovieLarge){
              ViewHolderMovieLarge viewHolderMovieLarge = (ViewHolderMovieLarge)holder;
              viewHolderMovieLarge.onBind(listData.get(position));
          } else if(holder instanceof  ViewHolderMovieSmall){
              ViewHolderMovieSmall viewHolderMovieSmall = (ViewHolderMovieSmall)holder;
              viewHolderMovieSmall.onBind(listData.get(position),selectedItems.get(position));
              viewHolderMovieSmall.setOnViewHolderItemClickListener(new OnViewHolderItemClickListener() {
                  @Override
                  public void onViewHolderItemClick() {
                      if (selectedItems.get(position)) {
                          // 펼쳐진 Item을 클릭 시
                          selectedItems.delete(position);
                      } else {
                          // 직전의 클릭됐던 Item의 클릭상태를 지움
                          selectedItems.delete(prePosition);
                          // 클릭한 Item의 position을 저장
                          selectedItems.put(position, true);
                      }
                      // 해당 포지션의 변화를 알림
                      if (prePosition != -1) notifyItemChanged(prePosition);
                      notifyItemChanged(position);
                      // 클릭된 position 저장
                      prePosition = position;
                  }
              });
          }
      }
  
      @Override
      public int getItemCount() {
          return listData.size();
      }
  
      void addItem(MyItem data) {
          // 외부에서 item을 추가시킬 함수입니다.
          listData.add(data);
      }
  }
  

 

MovieCase를 통해 큰 이미지로 보여줄 것인지, 작은 이미지로 보여줄것인지를 생성자를 통해서 대입을 해줍니다.

onCreateViewHolder에서 어떤 뷰로 보여줄 것인지 분기를 해줍니다.

 

Adapter 연결

이제 Activity에 Adapter를 연결해줍니다.

 

ActivityMovieLarge.java

  
  package com.example.recyclerviewtest;
  
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.recyclerview.widget.LinearLayoutManager;
  import androidx.recyclerview.widget.RecyclerView;
  
  import android.os.Bundle;
  import android.view.View;
  import android.widget.Button;
  
  public class ActivityMovieLarge extends AppCompatActivity {
  
      RecyclerVierAdapter adapter_large;
      RecyclerView recyclerView;
      Button btn_finish;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_movie_large);
          init();
          getData();
  
      }
  
      private void init(){
          recyclerView = findViewById(R.id.recyclerView);
          btn_finish = findViewById(R.id.btn_finish);
  
          LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
          recyclerView.setLayoutManager(linearLayoutManager);
  
          adapter_large = new RecyclerVierAdapter(MovieCase.LARGE);
          recyclerView.setAdapter(adapter_large);
  
          btn_finish.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  finish();
              }
          });
      }
  
      private void getData(){
          DataMovie data = new DataMovie(R.drawable.iron_man, "아이언맨");
          adapter_large.addItem(data);
          data = new DataMovie(R.drawable.spider_man, "스파이더맨");
          adapter_large.addItem(data);
          data = new DataMovie(R.drawable.black_panther, "블랙팬서");
          adapter_large.addItem(data);
          data = new DataMovie(R.drawable.doctor, "닥터스트레인지");
          adapter_large.addItem(data);
          data = new DataMovie(R.drawable.hulk, "헐크");
          adapter_large.addItem(data);
          data = new DataMovie(R.drawable.thor, "토르");
          adapter_large.addItem(data);
      }
  }
  

 

MainActivity.java

 
  package com.example.recyclerviewtest;
  
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.recyclerview.widget.LinearLayoutManager;
  import androidx.recyclerview.widget.RecyclerView;
  
  import android.content.Intent;
  import android.os.Bundle;
  import android.view.View;
  import android.widget.Button;
  
  public class MainActivity extends AppCompatActivity {
  
      RecyclerVierAdapter adapter_small;
      RecyclerView recyclerView;
      Button btn_show_large;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          init();
          getData();
  
      }
  
      private void init(){
          recyclerView = findViewById(R.id.recyclerView);
          btn_show_large = findViewById(R.id.btn_show_large);
  
          LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
          recyclerView.setLayoutManager(linearLayoutManager);
  
          adapter_small = new RecyclerVierAdapter(MovieCase.SMALL);
          recyclerView.setAdapter(adapter_small);
  
          btn_show_large.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  Intent intent = new Intent(getBaseContext(),ActivityMovieLarge.class);
                  startActivity(intent);
              }
          });
      }
  
      private void getData(){
          DataMovie data = new DataMovie(R.drawable.iron_man, "아이언맨");
          adapter_small.addItem(data);
          data = new DataMovie(R.drawable.spider_man, "스파이더맨");
          adapter_small.addItem(data);
          data = new DataMovie(R.drawable.black_panther, "블랙팬서");
          adapter_small.addItem(data);
          data = new DataMovie(R.drawable.doctor, "닥터스트레인지");
          adapter_small.addItem(data);
          data = new DataMovie(R.drawable.hulk, "헐크");
          adapter_small.addItem(data);
          data = new DataMovie(R.drawable.thor, "토르");
          adapter_small.addItem(data);
      }
  }
  

아답타를 생성할 때 

RecyclerVierAdapter adapter = new RecyclerVierAdapter(MovieCase.xxx);

이 부분을 통해 어떻게 분기될 것인지에 대해 지정을 해줍니다.

 

레이아웃

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">
  
      <LinearLayout
          android:orientation="horizontal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">
  
          <Button
              android:id="@+id/btn_show_large"
              android:text="크게보기"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"/>
  
      </LinearLayout>
  
      <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/recyclerView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>
  </LinearLayout>
  

 

activity_movie_large.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=".ActivityMovieLarge">
  
      <LinearLayout
          android:orientation="horizontal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">
  
          <Button
              android:id="@+id/btn_finish"
              android:text="종료"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"/>
  
      </LinearLayout>
  
      <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/recyclerView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>
  
  </LinearLayout>
  

 

item_movie_large.xml

 
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical">
  
      <ImageView
          android:id="@+id/iv_movie"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="250dp"/>
  
      <TextView
          android:paddingLeft="20dp"
          android:layout_gravity="center"
          android:gravity="center"
          android:textSize="16dp"
          android:id="@+id/tv_movie_title"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"/>
  </LinearLayout>
  

 

item_movie_small.xml

  
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical">
  
  
      <LinearLayout
          android:id="@+id/linearlayout"
          android:orientation="horizontal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content">
  
          <ImageView
              android:id="@+id/iv_movie"
              android:scaleType="centerCrop"
              android:layout_width="100dp"
              android:layout_height="80dp"/>
  
          <TextView
              android:paddingLeft="20dp"
              android:layout_gravity="center"
              android:gravity="center"
              android:textSize="16dp"
              android:id="@+id/tv_movie_title"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"/>
      </LinearLayout>
  
      <ImageView
          android:id="@+id/iv_movie2"
          android:scaleType="centerCrop"
          android:layout_width="match_parent"
          android:layout_height="250dp"/>
  
  
  </LinearLayout>
  

 

이처럼 Adapter를 모듈화를 하게 되면 하나의 Adapter를 가지고 여러개의 RecyclerView를 생성할 수 있습니다.

 

궁금하신점이나 잘못된점이 있다면 댓글로 남겨주시면 답변달아드리겠습니다~~

Comments