2024. 4. 9. 16:38, ๐ฑAndroid Study
๋ฐ์ํ
1. RecyclerView
- ์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๋ทฐ
- ์๋๋ก์ด๋ ์ฑ์์ ๋ฆฌ์คํธ ํํ์ ๋ฐ์ดํฐ๋ฅผ ํ์ํ๋๋ฐ ์ฌ์ฉ๋๋ ์์ ฏ
- ์ฌ๋ฌ ์์ดํ ์ ์คํฌ๋กค ๊ฐ๋ฅํ ๋ฆฌ์คํธ๋ก ํํํ๋ฉฐ, ๋ง์ ์์ดํ ์ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ๋ณด์ฌ์ฃผ๋ ์ญํ
- ํ์ ์ ์ธ ํ๋ฉด์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ ์ ์๋ View
- Recycle์ ํ๊ตญ์ด๋ก ํ๋ฉด ์ฌํ์ฉํ๋ค๋ผ๋ ๋ป → View๋ฅผ ์ฌํ์ฉํด์ ์ฌ์ฉ
02. ListView ์ RecyclerView์ ์ฐจ์ด์
2-1. ListView
- ์ฌ์ฉ์๊ฐ ์คํฌ๋กค ํ ๋๋ง๋ค ์์ ์๋ ์์ดํ ์ ์ญ์ ๋๊ณ , ๋งจ ์๋์ ์์ดํ ์ ์์ฑ ๋๊ธธ ๋ฐ๋ณต
- ์์ดํ ์ด 100๊ฐ๋ฉด 100๋ฒ ์ญ์ ๋ฐ ์์ฑ
- ๊ณ์ ์ญ์ ์ ์์ฑ์ ๋ฐ๋ณตํ๋ฏ๋ก ์ฑ๋ฅ์ ์ข์ง ์์
2.2 RecyclerView
- ์ฌ์ฉ์๊ฐ ์คํฌ๋กค ํ ๋, ์์ ์๋ ์์ดํ ์ ์ฌํ์ฉ ๋ผ์ ์๋๋ก ์ด๋ํ์ฌ ์ฌ์ฌ์ฉ
- ์ฆ ์์ดํ ์ด 100๊ฐ์ฌ๋ 10๊ฐ์ ๋์ View๋ง ๋ง๋ค๊ณ 10๊ฐ๋ฅผ ์ฌํ์ฉํด์ ์ฌ์ฉ
- View๋ฅผ ๊ณ์ ๋ง๋๋ ListView์ ๋จ์ ์ ๋ณด์ํ๊ธฐ ์ํด ๋ฑ์ฅ
03. RecyclerView ์ฌ์ฉ๋ฒ
๐ก Adapter
- ๋ฐ์ดํฐ ํ ์ด๋ธ์ ๋ชฉ๋ก ํํ๋ก ๋ณด์ฌ์ฃผ๊ธฐ ์ํด ์ฌ์ฉ๋๋ ๊ฒ
- ๋ฐ์ดํฐ๋ฅผ ๋ค์ํ ํ์์ ๋ฆฌ์คํธ ํ์์ผ๋ก ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์ ๋ฐ์ดํฐ์ RecyclerView ์ฌ์ด์ ์กด์ฌํ๋ ๊ฐ์ฒด
- ๋ฐ์ดํฐ์ RecyclerView ์ฌ์ด์ ํต์ ์ ์ํ ์ฐ๊ฒฐ์ฒด
๐ก ViewHolder
- ํ๋ฉด์ ํ์๋ ๋ฐ์ดํฐ๋ ์์ดํ ๋ค์ ์ ์ฅํ๋ ์ญํ
- ์คํฌ๋กค ํด์ ์๋ก ์ฌ๋ผ๊ฐ View๋ฅผ ์ฌํ์ฉํ๊ธฐ ์ํด์ ์ด View๋ฅผ ๊ธฐ์ตํ๋ ์ญํ
2-1. XML ์ ์
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView // ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ XML์ ์
๋ ฅ
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
2-2. ์ด๋ํฐ ํด๋์ค์ ์
- ์์ ์ ์ํ MyItem ํ์
์ ๊ฐ์ฒด๋ค์ ArrayList๋ก ๊ด๋ฆฌํ๋ MyAdapterํด๋์ค๋ฅผ
RecyclerView.Adapter๋ก ํ์ํ์ฌ ์ ์
MyAdapter.kt
class MyAdapter(val mItems: MutableList<MyItem>) : RecyclerView.Adapter<MyAdapter.Holder>() {
interface ItemClick {
fun onClick(view : View, position : Int)
}
var itemClick : ItemClick? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.itemView.setOnClickListener { //ํด๋ฆญ์ด๋ฒคํธ์ถ๊ฐ๋ถ๋ถ
itemClick?.onClick(it, position)
}
holder.iconImageView.setImageResource(mItems[position].aIcon)
holder.name.text = mItems[position].aName
holder.age.text = mItems[position].aAge
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemCount(): Int {
return mItems.size
}
inner class Holder(val binding: ItemRecyclerviewBinding) : RecyclerView.ViewHolder(binding.root) {
val iconImageView = binding.iconItem
val name = binding.textItem1
val age = binding.textItem2
}
}
2-3. ๋ฉ์ธ์กํฐ๋นํฐ ์ ์
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// ๋ฐ์ดํฐ ์๋ณธ ์ค๋น
val dataList = mutableListOf<MyItem>()
dataList.add(MyItem(R.drawable.sample_0, "Bella", "1"))
dataList.add(MyItem(R.drawable.sample_1, "Charlie", "2"))
dataList.add(MyItem(R.drawable.sample_2, "Daisy", "1.5"))
dataList.add(MyItem(R.drawable.sample_3, "Duke", "1"))
dataList.add(MyItem(R.drawable.sample_4, "Max", "2"))
dataList.add(MyItem(R.drawable.sample_5, "Happy", "4"))
dataList.add(MyItem(R.drawable.sample_6, "Luna", "3"))
dataList.add(MyItem(R.drawable.sample_7, "Bob", "2"))
binding.recyclerView.adapter = MyAdapter(dataList)
val adapter = MyAdapter(dataList)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
adapter.itemClick = object : MyAdapter.ItemClick {
override fun onClick(view: View, position: Int) {
val name: String = dataList[position].aName
Toast.makeText(this@MainActivity," $name ์ ํ!", Toast.LENGTH_SHORT).show()
}
}
}
}
โจ ๋ทฐ ๋ฐ์ธ๋ฉ์ ์ด์ฉํ ์ต์ ์์, ๋ฉํฐ ๋ทฐํ์
๋๋ณด๊ธฐ
์ด๋ํฐ
package com.limheejin.camp_standard4.presentation
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.limheejin.camp_standard4.data.CardData
import com.limheejin.camp_standard4.data.priceDecimal
import com.limheejin.camp_standard4.databinding.ItemCard1Binding
import com.limheejin.camp_standard4.databinding.ItemCard2Binding
import com.limheejin.camp_standard4.databinding.ItemCard3Binding
class CardAdapter(private val cardItems: List<CardData>) : // ์ด๋ํฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋, ๋ง๋ค์ด๋์ ๋ฐ์ดํฐ ํด๋์ค๋ฅผ ์ธ์๋ก ๋ฐ์์ค
RecyclerView.Adapter<RecyclerView.ViewHolder>() { // RecyclerView.Adapter<์๋์ ๋ง๋ค ๋ทฐํ๋ ํด๋์ค>๋ฅผ ์์ (๋ทฐํ๋ ํจํด์ผ๋ก ๊ตฌํ)
interface ItemClick {
fun onClick(view: View, position: Int)
}
var itemClick: ItemClick? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { // ์์์ ๋ง๋ ๋ทฐํ๋์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ถ๋ถ, 10๋ฒ ์ ๋ ์คํ๋๊ณ ์ ๋จ
// ์์ดํ
์ ๋ด์ xml์ ๋ทฐ ๋ฐ์ธ๋ฉ ๊ฐ์ฒด ์์ฑ
return when(viewType){
TYPE_1 -> {
val binding = ItemCard1Binding.inflate(LayoutInflater.from(parent.context), parent, false)
MultiViewHolder1(binding)
}
TYPE_2 -> {
val binding = ItemCard2Binding.inflate(LayoutInflater.from(parent.context), parent, false)
MultiViewHolder2(binding)
}
else -> {
val binding = ItemCard3Binding.inflate(LayoutInflater.from(parent.context), parent, false)
MultiViewHolder3(binding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { // ์ ๋ณด๋ฅผ ๋ฐ์ธ๋ฉ ํ๋ ๋ถ๋ถ. ์ฒ์ ์ด๋ํฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ์ธ์๋ก ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ, ์์ ๋ทฐํ๋ ๊ฐ์ฒด์ ์ด๋ป๊ฒ ๋ฃ์ด์ค์ง ๊ฒฐ์ ํ๋ ๋ถ๋ถ
//ํฌ์ง์
์ ๋ฆฌ์คํธ์์ ์ด๋ ์์น์ ์๋์ง
var currentItem = cardItems[position]
when(holder.itemViewType){
TYPE_1 -> {
val viewHolder = holder as MultiViewHolder1
viewHolder.bind(currentItem)
// holder.itemView.setOnClickListener {
// onClick(currentItem)
// }
}
TYPE_2 -> {
val viewHolder = holder as MultiViewHolder2
viewHolder.bind(currentItem)
}
TYPE_3 -> {
val viewHolder = holder as MultiViewHolder3
viewHolder.bind(currentItem)
}
}
}
override fun getItemViewType(position: Int): Int { // position๋ง๋ค ์ด๋ค ๋ทฐํ์
์ ๊ฐ์ง๊ณ ์๋์ง ์ฐ๊ฒฐํด์ค์ผ ํจ
return when (position) {
0 -> TYPE_1
1 -> TYPE_2
2 -> TYPE_3
else -> {
throw IllegalAccessException("ERROR")
}
}
}
override fun getItemCount(): Int { // ๋ฐ์ดํฐ๊ฐ ๋ช ๊ฐ์ธ์ง ๋ณํ
// ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์์ดํ
๊ฐ์๋ ๋ฐ์ดํฐ ๋ฆฌ์คํธ์ ํฌ๊ธฐ
return cardItems.size
}
// ๊ฐ๋จํ๊ฒ override fun getItemCount(): Int = cardItems.size ํ ์ค์ง๋ฆฌ ์ฝ๋๋ก ์
๋ ฅํด๋ ๋จ
inner class MultiViewHolder1(private val binding: ItemCard1Binding) : // ๋ทฐํ๋ ํด๋์ค๋ฅผ ์์ฑ, RecyclerView.ViewHolder ํด๋์ค๋ฅผ ์์ํจ. binding: ์์ดํ
xml์ด๋ฆ + Binding
RecyclerView.ViewHolder(binding.root) {
// ๋ทฐ ๋ฐ์ธ๋ฉ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ root ๋ณ์๋ ๋ ์ด์์์ ๋ฃจํธ ๋ ์ด์์์ ์๋ฏธ
fun bind(card: CardData){
val context = binding.root.context //์ํฐํจํด์ด๋ผ ์ง์์ผ๋จ
binding.tvName.text = card.name
binding.tvCardNumber.text = card.cardNumber
binding.tvExpDate.text = card.expDate
binding.tvPrice.text = priceDecimal(card.price)
binding.itemCard1.setOnClickListener { // ์กํฐ๋นํฐ์์ ๊ตฌํํด์ผ ํจ
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra("CardData", card)
context.startActivity(intent)
}
}
}
inner class MultiViewHolder2(private val binding: ItemCard2Binding) : // ๋ทฐํ๋ ํด๋์ค๋ฅผ ์์ฑ, RecyclerView.ViewHolder ํด๋์ค๋ฅผ ์์ํจ. binding: ์์ดํ
xml์ด๋ฆ + Binding
RecyclerView.ViewHolder(binding.root) {
// ๋ทฐ ๋ฐ์ธ๋ฉ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ root ๋ณ์๋ ๋ ์ด์์์ ๋ฃจํธ ๋ ์ด์์์ ์๋ฏธ
fun bind(card: CardData){
val context = binding.root.context //์ํฐํจํด์ด๋ผ ์ง์์ผ๋จ
binding.tvName.text = card.name
binding.tvCardNumber.text = card.cardNumber
binding.tvExpDate.text = card.expDate
binding.tvPrice.text = priceDecimal(card.price)
binding.itemCard2.setOnClickListener { // ์กํฐ๋นํฐ์์ ๊ตฌํํด์ผ ํจ
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra("CardData", card)
context.startActivity(intent)
}
}
}
inner class MultiViewHolder3(private val binding: ItemCard3Binding) : // ๋ทฐํ๋ ํด๋์ค๋ฅผ ์์ฑ, RecyclerView.ViewHolder ํด๋์ค๋ฅผ ์์ํจ. binding: ์์ดํ
xml์ด๋ฆ + Binding
RecyclerView.ViewHolder(binding.root) {
// ๋ทฐ ๋ฐ์ธ๋ฉ์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ root ๋ณ์๋ ๋ ์ด์์์ ๋ฃจํธ ๋ ์ด์์์ ์๋ฏธ
fun bind(card: CardData){
val context = binding.root.context //์ํฐํจํด์ด๋ผ ์ง์์ผ๋จ
binding.tvName.text = card.name
binding.tvCardNumber.text = card.cardNumber
binding.tvExpDate.text = card.expDate
binding.tvPrice.text = priceDecimal(card.price)
binding.itemCard3.setOnClickListener { // ์กํฐ๋นํฐ์์ ๊ตฌํํด์ผ ํจ
val intent = Intent(context, DetailActivity::class.java)
intent.putExtra("CardData", card)
context.startActivity(intent)
}
}
}
}
๋ฉ์ธํ์ด์ง
package com.limheejin.camp_standard4.presentation
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.limheejin.camp_standard4.data.CardData
import com.limheejin.camp_standard4.data.CardList
import com.limheejin.camp_standard4.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val cardList = CardList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setRecyclerView()
}
private fun setRecyclerView() { // ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ ์ค์
binding.rvRecyclerview.adapter = CardAdapter(cardList)
// xml ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ์ ์ด๋ํฐ๋ก ์์์ ๋ง๋ ์ด๋ํฐ ์ค์
binding.rvRecyclerview.layoutManager = LinearLayoutManager(this)
// xml ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ์ ๋ ์ด์์ ๋งค๋์ ์ค์
}
// private fun adapterOnClick(card : Card) {
// //bundle๋ก ๋๊ธฐ๋์์
// val intent = Intent(this, DetailActivity::class.java)
// val bundle = Bundle().apply{
// putParcelable(DetailActivity.EXTRA_CARD, card)
// }
// intent.putExtra(bundle)
// startActivity(intent)
// }
}
๋ํ ์ผ ํ์ด์ง
package com.limheejin.camp_standard4.presentation
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.limheejin.camp_standard4.data.CardData
import com.limheejin.camp_standard4.data.priceDecimal
import com.limheejin.camp_standard4.databinding.ActivityDetailBinding
class DetailActivity : AppCompatActivity() {
private lateinit var binding: ActivityDetailBinding
// companion object {
// const val EXTRA_CARD: String = "extra_card"
// }
private val selectedCard by lazy {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){
intent.getParcelableExtra("CardData", CardData::class.java)
} else {
intent.getParcelableExtra("CardData")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
selectedCard?.let {
binding.tvName.text = it.name
binding.tvCardNumber.text = it.cardNumber
binding.tvExpDate.text = it.expDate
binding.tvPrice.text = priceDecimal(it.price)
}
// val cardItem = intent.getParcelableArrayExtra<Card>(EXTRA_CARD)
//
// cardItem?.name ๋ฑ๋ฑ
}
}
xml ์์ ๋ฆฌ์ฌ์ดํด๋ฌ๋ทฐ
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="@layout/item_card1" />
๋ฐ์ํ
๐ฌ C O M M E N T