Swipe Menu with RecyclerView - 데이터 구성
리싸이클러 뷰 내부에서 item을 슬라이드할 때 DELETE 버튼이 나오도록 코드를 작성한다.
메시지 목록에서 슬라이드해서 메시지를 삭제하는 등에 사용되는 기능이다.
일반적으로 삭제 버튼을 숨겨두었다가, 필요시에 스와이프해서 사용한다.
CSV 데이터 읽기
우선 recycler view에 나타낼 데이터를 선정한다.
https://www.kaggle.com/datasets/
위의 링크에서 여러 데이터셋을 무료로 사용할 수 있다.
영화에 대한 정보를 가지고 있는 IMDB movies dataset을 사용했다.
https://www.kaggle.com/datasets/ashpalsingh1525/imdb-movies-dataset?resource=download
안드로이드 스튜디오에서 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"))