코딩스토리

[Android/안드로이드] Expandable RecyclerView 접기/펼치기(아코디언) 본문

Android/유용한 기술

[Android/안드로이드] Expandable RecyclerView 접기/펼치기(아코디언)

라크라꾸 2019. 12. 20. 17:55

이전 글에서 리사이클러뷰 사용법에 대해 설명을 했었습니다.

이번 글에서는 리사이클러뷰를 클릭했을 때 접고, 펼칠 수 있는 기능을 추가해 보도록 하겠습니다.

 

이전 글에서 만들어 놓았던 DataMovie클래스와 MainActivity클래스는 수정할 부분이 없으므로 생략하고 수정사항이 있는 부분말 적어놓도록 하겠습니다. DataMovie클래스와 MainActivity부분이 없으신 분들은 이전 글을 참조해주세요.

 

2019/12/19 - [Android/유용한 기술] - [Android/안드로이드] RecyclerView(리사이클러뷰) 뷰 재활용하기

 

[Android/안드로이드] RecyclerView(리사이클러뷰) 뷰 재활용하기

기존의 ListView는 커스터마이징 하기에도 힘들었고, 구조적인 문제로 성능상의 문제가 있었기 때문에 보다 유연하고 성능이 향상된 RecyclerView가 생겨났습니다. RecyclerView와 ListView의 가장 큰 차이점은 리..

lakue.tistory.com

 

우선 뷰홀더에 있는 버튼을 클릭했을 경우 Adapter에서도 알 수 있도록 이벤트 리스너를 만들겠습니다.

 

이벤트 리스너 생성

OnViewHolderItemClickListener.java

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

 

 

이제 Adapter에서 클릭했을 때 접고, 펼칠 수 있도록 아코디언 형식을 만들어 보겠습니다.

 

RecyclerVierAdapter.java

    
    package com.lakue.recyclerviewarcodian;
    
    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<RecyclerView.ViewHolder> {
    
        // adapter에 들어갈 list 입니다.
        private ArrayList<DataMovie> listData = new ArrayList<>();
    
        // Item의 클릭 상태를 저장할 array 객체
        private SparseBooleanArray selectedItems = new SparseBooleanArray();
        // 직전에 클릭됐던 Item의 position
        private int prePosition = -1;
    
    
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_movie, parent, false);
            return new ViewHolderMovie(view);
        }
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
            ViewHolderMovie viewHolderMovie = (ViewHolderMovie)holder;
            viewHolderMovie.onBind(listData.get(position),position, selectedItems);
            viewHolderMovie.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(DataMovie data) {
            // 외부에서 item을 추가시킬 함수입니다.
            listData.add(data);
        }
    }
    

이전 소스코드와 다른점은 SparseBooleanArray형의 객체를 생성했고, 이전에 클릭했던 아이템의 position을 저장하는 int형 prePosition 변수를 생성했습니다.

onBind부분에서 아까 생성했던 selectedItem객체의 position번째값을 넘겨주도록 합니다.

setOnViewHolderItemClickListener부분은 이제 ViewHolder에서 해당 Item을 클릭했을 경우 발생하는 리스너입니다.

클릭했을 경우 사용되는 기능은 주석을 통해 설명하였습니다.

 

ViewHolderMovie.java

    
    package com.lakue.recyclerviewarcodian;
    
    import android.animation.ValueAnimator;
    import android.util.SparseBooleanArray;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;
    
    public class ViewHolderMovie extends  RecyclerView.ViewHolder {
    
        TextView tv_movie_title;
        ImageView iv_movie,iv_movie2;
        LinearLayout linearlayout;
    
        OnViewHolderItemClickListener onViewHolderItemClickListener;
    
    
        public ViewHolderMovie(@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(DataMovie data,int position, SparseBooleanArray selectedItems){
            tv_movie_title.setText(data.getTitle());
            iv_movie.setImageResource(data.getImage());
            iv_movie2.setImageResource(data.getImage());
            changeVisibility(selectedItems.get(position));
        }
    
        private void changeVisibility(final boolean isExpanded) {
            // ValueAnimator.ofInt(int... values)는 View가 변할 값을 지정, 인자는 int 배열
            ValueAnimator va = isExpanded ? ValueAnimator.ofInt(0, 600) : ValueAnimator.ofInt(600, 0);
            // Animation이 실행되는 시간, n/1000초
            va.setDuration(500);
            va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // imageView의 높이 변경
                    iv_movie2.getLayoutParams().height = (int) animation.getAnimatedValue();
                    iv_movie2.requestLayout();
                    // imageView가 실제로 사라지게하는 부분
                    iv_movie2.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
                }
            });
            // Animation start
            va.start();
        }
    
        public void setOnViewHolderItemClickListener(OnViewHolderItemClickListener onViewHolderItemClickListener) {
            this.onViewHolderItemClickListener = onViewHolderItemClickListener;
        }
    }
    

이전 글과는 UI가 조금 바뀌습니다. 아까 생성했던 이벤트 리스너를 선언해주고, 레이아웃을 클릭했을 때 onViewHolderItemClickListener.onViewHolderItemClick()를 호출하여 이벤트를 발생시킵니다. 발생된 이벤트는 RecyclerViewAdapter에서 setOnViewHolderItemClickListener로 받아서 처리하였습니다.

 

changeVisibility(final boolean isExpanded)함수에서는 adapter에서 받아온 isSelect값이 true이면 이미지를 보여주고, false일 경우 이미지를 gone시켜주는 기능을 하고있습니다.

 

item_movie.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:id="@+id/linearlayout"
    android:orientation="vertical">


    <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:padding="20dp"
            android:gravity="center"
            android:textSize="16dp"
            android:id="@+id/tv_movie_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </LinearLayout>

    <ImageView
        android:visibility="gone"
        android:id="@+id/iv_movie2"
        android:scaleType="centerCrop"
        android:layout_width="match_parent"
        android:layout_height="200dp"/>

</LinearLayout>

 

결과화면

 

다음과 같이 아이템을 클릭하면 RecyclerView를 접었다 폈다 할 수 있는 기능을 구현해보았습니다. 

 

궁금하신점이나 제가 실수한 부분이 있다면 댓글로 남겨주세요~~

 

코드작동 안되시는분들을 위해 Git에 저장해놓았습니다. 참고해주세요~~

https://github.com/lakue119/RecyclerVIewAccordion

Comments