[Android, Kotlin] RecyclerView에서 Swipe Menu 구현하기, Delete 메뉴 + Card View [1]

 

Swipe Menu with RecyclerView - 데이터 구성 

리싸이클러 뷰 내부에서 item을 슬라이드할 때 DELETE 버튼이 나오도록 코드를 작성한다.

메시지 목록에서 슬라이드해서 메시지를 삭제하는 등에 사용되는 기능이다.

일반적으로 삭제 버튼을 숨겨두었다가, 필요시에 스와이프해서 사용한다.

 

리싸이클러 뷰에서 삭제버튼

 

 

CSV 데이터 읽기

 

우선 recycler view에 나타낼 데이터를 선정한다.

 

https://www.kaggle.com/datasets/

 

Find Open Datasets and Machine Learning Projects | Kaggle

Download Open Datasets on 1000s of Projects + Share Projects on One Platform. Explore Popular Topics Like Government, Sports, Medicine, Fintech, Food, More. Flexible Data Ingestion.

www.kaggle.com

위의 링크에서 여러 데이터셋을 무료로 사용할 수 있다.

 

영화에 대한 정보를 가지고 있는 IMDB movies dataset을 사용했다.

https://www.kaggle.com/datasets/ashpalsingh1525/imdb-movies-dataset?resource=download 

 

IMDB movies dataset

Explore 10000+ movies worldwide with the IMDB Movies dataset

www.kaggle.com

 

안드로이드 스튜디오에서 csv 파일을 사용하기 위해서는 몇가지 설정이 필요하다.

 

우선 csv 파일을 읽기 위한 의존성을 추가한다.

implementation 'com.opencsv:opencsv:5.6'

csv 파일을 안드로이드 프로젝트 내부에 넣어줘야 하는데, 이를 위해서 asset 폴더를 생성한다.

 

위와 같이 메뉴를 이동해서 Assets Folder를 생성하고, 읽어 올 csv 파일을 폴더안에 넣어준다.

assets 파일을 읽기 위해서는 assetManager를 사용해야 하며, open을 통해서 inputStream을 가져온 후,

CSVReader의 inputStreamReader를 통해서 데이터를 읽어온다.

 

        val assertManager = this@SwipeCardViewActivity.assets
        val inputStream = assertManager.open("imdb_movies.csv")
        val csvReader = CSVReader(InputStreamReader(inputStream,"EUC-KR"))

        val dataList =csvReader.readAll()
        for (data in dataList){
            dataSet!!.add(MovieData(
                data[0].toString(), // 이름
                data[1].toString(), // data_x
                data[2].toString(), // score
                data[3].toString() // genre
            ))
        }
        dataSet!!.removeAt(0)

 

위와 같은 방식으로 데이터를 읽게 되는데, dataList (ArrayList<MovieData>) 를 선언해서 각 데이터를 넣어준다.

모든 데이터를 넣어준 후 반드시 첫 데이터를 제거해야 한다.

첫 데이터로는 목차 목록이 들어가기 때문이며, 인덱스를 통해서 열에 해당하는 속성 값을 가져올 수 있다.

사용 된 movieData는 아래와 같다.

data class MovieData(
    val name : String,
    val date_x : String,
    val score : String,
    val genre : String,
)

 

Card View와 Recycler View, Adapter 생성

이제 데이터 셋을 준비했으니, View를 생성해야 한다.

리싸이클러 뷰와 각 리싸이클러 뷰에 사용 될 카드뷰를 제작한다.

 

<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:padding="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/name"
            tools:text = "name "
            android:textStyle="bold"
            android:textSize="25sp"
            android:lines="1"
            android:ellipsize="end"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="250dp"
            android:layout_height="wrap_content"/>
        <TextView
            tools:text="15May03"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/score"
            android:id="@+id/data_x"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:id="@+id/score"
            tools:text="100"
            app:layout_constraintEnd_toEndOf="parent"
            android:textSize="35sp"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
        <TextView
            android:textSize="20sp"
            tools:text="genre"
            android:id="@+id/genre"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/name"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_width="250dp"
            android:lines="1"
            android:ellipsize="end"
            android:layout_height="wrap_content"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

movieData 클래스에 있는 속성들을 보여주기 위한 카드 뷰를 작성한다.

카드 뷰를 사용하기 위해서는 아래와 같이 의존성을 추가해야 한다.

    // 카드뷰
    implementation "androidx.cardview:cardview:1.0.0"

 

데이터를 보여줄 액티비티에서 리싸이클러 뷰를 생성하고, 코드 상에서 연결시킨다.

    <androidx.recyclerview.widget.RecyclerView
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/card_view"
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
        adapter = CardViewAdapter(this@SwipeCardViewActivity, dataSet!!)
        binding.recyclerView.adapter= adapter

 

리싸이클러 뷰를 나타내기 위해서 어댑터를 사용해야 하는데, CardViewAdapter 클래스를 별도로 제작했다.

해당 클래스에서는 리싸이클러 뷰의 각 데이터를 나타내는 카드뷰를 binding을 통해서 데이터를 연결해준다.

delData() 함수를 선언해서, 나중에 삭제 요청이 들어오면 이 함수를 통해서 데이터를 삭제하고 리스트를 정렬하는 기능을 한다.

class CardViewAdapter(
    private val activity: SwipeCardViewActivity,
    private val dataSet : ArrayList<MovieData>
) :RecyclerView.Adapter<CardViewAdapter.ViewHolder>(){
    private lateinit var binding : CardViewBinding

    inner class ViewHolder(
        binding: CardViewBinding
    ) : RecyclerView.ViewHolder(binding.root){
        fun hold(){
            val item = dataSet[absoluteAdapterPosition]
            Log.d("data in rey",item.toString())
            binding.name.text = item.name
            binding.dataX.text = item.date_x
            binding.genre.text = item.genre
            binding.score.text = item.score
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        binding = CardViewBinding.inflate(LayoutInflater.from(activity),
        parent,false)
        return ViewHolder(binding)
    }

    override fun getItemCount(): Int =dataSet.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.hold()
    }

    fun delData(position: Int){
        dataSet.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position,itemCount)
    }
}

 

아래와 같은 카드뷰를 아이템으로 가지는  recycler view를 생성하였으며,

다음 포스팅에서 ItemTouchHelper를 사용하여 스와이프 기능을 추가하는 방법을 포스팅한다.

 

영문으로 된 csv 파일을 읽어서 인코딩 하였으므로, 혻과 같은 의도치 않은 번역 오류가 발생했다.

이를 해결하기 위해서는 아래에서 EUC-KR 부분을 제거한다.

        val csvReader = CSVReader(InputStreamReader(inputStream,"EUC-KR"))