RecyclerView 는 안드로이드 개발을 할 때 자주 사용하게 된다.
그런데 만들때마다 조금씩 헷갈릴 때가 있어서, 하나하나 깔끔하게 과정을 정리하여 참고할 수 있는 라이브러리처럼 만들어 보고자 한다.
또한 RecyclerView 에 ViewBinding 을 사용해보고자 한다.
일단 메인액티비티 레이아웃에 리싸이클러뷰를 추가해주고,
리싸이클러뷰에 표현될 아이템 형식을 지정할 레이아웃을 추가해준다.
<?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="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" />
<TextView
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="16dp"
android:text="TextView"
android:textSize="30sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/description_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@+id/name_text"
app:layout_constraintTop_toBottomOf="@+id/name_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
다음과 같이 이미지 하나와 제목, 설명이 들어가는 아이템 뷰를 작성하였다.
// 익스텐션 대신 뷰바인딩
buildFeatures {
viewBinding true
}
bulid.gradle 의 android 에 추가해준다.
<resources>
<string name="app_name">RecyclerViewEx</string>
<string-array name="item_name">
<item>Item 1</item>
<item>Item 2</item>
<item>Item 3</item>
<item>Item 4</item>
<item>Item 5</item>
<item>Item 6</item>
<item>Item 7</item>
<item>Item 8</item>
</string-array>
<string-array name="item_description">
<item>Item 1 입니다.</item>
<item>Item 2 입니다.</item>
<item>Item 3 입니다.</item>
<item>Item 4 입니다.</item>
<item>Item 5 입니다.</item>
<item>Item 6 입니다.</item>
<item>Item 7 입니다.</item>
<item>Item 8 입니다.</item>
</string-array>
</resources>
values 의 strings 에 임의의 제목, 설명을 8가지 지정해주었다.
class MyAdapter(val nameList : Array<String>, val desList : Array<String>)
: RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
// 생성된 뷰 홀더에 값 지정
class MyViewHolder(val binding: RecyclerviewItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(name: String, des: String) {
// 뷰 홀더의 제목과 설명
binding.nameText.text = name
binding.descriptionText.text = des
// 뷰 홀더 클릭시 디테일뷰로
binding.cardView.setOnClickListener {
val intent: Intent = Intent(it.context, DetailViewActivity::class.java)
intent.putExtra("currentName", name)
intent.putExtra("currentDes", des)
it.context.startActivity(intent)
}
}
}
// 어떤 xml 으로 뷰 홀더를 생성할지 지정
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = RecyclerviewItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return MyViewHolder(binding)
}
// 뷰 홀더에 데이터 바인딩
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(nameList[position],desList[position])
}
// 뷰 홀더의 개수 리턴
override fun getItemCount(): Int {
return nameList.size
}
}
리싸이클러뷰에 연결해줄 어댑터를 생성한다.
하나씩 살펴보면
리싸이클러뷰 어댑터를 상속받는다.
파라미터로는 제목 리스트와 설명 리스트를 받도록 한다.
상속받은 어댑터에서 3개의 메소드를 구현해야하는데,
onCreateViewHolder 는 어떤 xml로 뷰 홀더를 생성할지 지정해주는 메소드이다.
여기서 뷰 바인딩을 사용하여 바인딩한다.
onBindViewHolder 는 뷰 홀더에 데이터를 바인딩 해주는 부분이며 커스텀해준 뷰 홀더의 메소드를 호출할 것이다.
getItemCount 는 뷰 홀더의 개수를 리턴해주는 메소드로 인자로 사용한 갯수를 리턴해주면 된다.
여기서 파라미터로 바인딩을 받아주며 뷰홀더에서 바인딩한 부분의 root 를 상속받아 뷰를 만든다.
bind 메소드를 작성하여 뷰 홀더마다 제목과 설명을 할당해주고, 뷰 홀더를 클릭 시 인텐트를 생성하여 Extra 값을 넘겨줘 그 값을 받아 출력하는 별도의 창이 뜨도록 할 것이다.
어댑터에서 인텐트의 context(it.context) 는 binding.cardView.context 이다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".DetailViewActivity">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="170dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_launcher_foreground" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="93dp"
android:text="TextView"
android:textSize="32sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="62dp"
android:text="TextView"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
액티비티를 하나 만들어서, 여기에 뷰 홀더에 따른 제목과 설명을 출력할 것이다.
class DetailViewActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailViewBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 뷰 바인딩
binding = ActivityDetailViewBinding.inflate(layoutInflater)
setContentView(binding.root)
getData()
}
// 클릭된 뷰에 관한 데이터를 보여줌
fun getData(){
if(intent.hasExtra("currentName") && intent.hasExtra("currentDes")){
binding.textView.text = intent.getStringExtra("currentName")
binding.textView2.text = intent.getStringExtra("currentDes")
}
else{
Toast.makeText(this,"불러오기 실패",Toast.LENGTH_SHORT).show()
}
}
}
뷰 바인딩을 해주고
뷰 홀더에서 넘겨준 Extra 값을 받아와 텍스트에 할당해준다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var nameList : Array<String>
lateinit var descriptionList : Array<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 뷰 바인딩
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 이름과 설명 초기화
nameList = resources.getStringArray(R.array.item_name)
descriptionList = resources.getStringArray(R.array.item_description)
// 아이템을 가로로 하나씩 보여줌
binding.recyclerView.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
// 어댑터 연결
binding.recyclerView.adapter = MyAdapter(nameList,descriptionList)
}
}
처음에 작성한 리스트들을 할당하여 어댑터에 연결해준다.
잘 구현 되었다.
https://github.com/HanYeop/AndroidStudio-Practice/tree/master/RecyclerViewEx
참조
https://www.youtube.com/watch?v=18VcnYN5_LM