Adapter Pattern
- 데이터 소스가 다양한 경우 유용함
- 해당 데이터 소스를 처리할 수 있도록 인터페이스 구현
- getItemCount()
- onCreateViewHolder()
- onBindViewHolder()
Recycler View
- ListView보다 발전된 View
- ListView를 사용하면서 속도를 향상시키기 위해 개발자들이 사용하던 방법을 포함하여 클래스로 제공
- RecyclerView를 사용하면 개발자들이 최적화 작업을 안 해도 됨
- 스크롤되면서 화면 밖으로 밀려나 사라지는 리스트 아이템을 재활용
- 화면에 표시되는 수의 아이템 View만으로 전체 리스트 처리 가능
- onCreateViewHolder(): 처음에 아이템 View 생성 시에만 호출 (밀려난 View는 재활용)
- onBindViewHolder(): Item View에 값 지정
RecyclerView Programming
//activity_main.xml
//RecyclerView 추가
<androidx.recyclerview.widget.RecyclerView
android:id = "@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
//ainActivity.kt
//SongAdapter 추가
//list의 adapter에 SongAdapter 할당
inner class SongAdapter : RecyclerView.Adapter<SongAdapter.ViewHolder>() {
inner class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
val txSong : TextView = itemView.findViewById(android.R.id.text1)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
//inflation
val view = LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false)
// fasle: 만들자마자 parent에 바로 붙일거냐? 우리는 어뎁터 사용하니까 false
// 이 클래스가 activity가 아닌 songAdaptor이기 때문에 this 못 넣음. context switching
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//텍스트뷰 값을 세팅하도록 함 - vh가 텍스트뷰 가짐
holder.txSong.text = model.list.value?.get(position) ?: ""
//model.list.value[position] 이 제일 깔끔하지만 value가 nullable이기 때문에 이렇게 사용할 수 없음
}
override fun getItemCount() = model.list.value?.size ?: 0
}
//ListViewModel.kt
//ViewModel 추가
class ListViewModel : ViewModel() {
private val songs = ArrayList<String>()
private val _list = MutableLiveData<ArrayList<String>>() //public으로 바꿔서 사용 가능하나 캡슐화를 위해 private
val list : LiveData<ArrayList<String>>
get() = _list
init {
_list.value = songs
}
// fun getList(): LiveData<ArrayList<String>> = _list //mutable이 아닌 그냥 liveData를 주게 함 (값만 가져가도록 읽기 전용)
fun add(song: String) {
songs.add(song)
_list.value = songs
}
// fun getSong(i: Int) = songs[i]
// fun getSize() = songs.size
}
//MainActivity.kt
private lateinit var model : ListViewModel
private val songAdaptor = SongAdapter()
@SuppressLint("NotifyDataSetChanged") //에러 나는 거 알고 있으니까 걍 쓸게
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
model = ViewModelProvider(this)[ListViewModel::class.java]
//어떤 방식으로 recyclerView가 동작하는지 설정해줘야함
/*
songAdaptor()과 songAdaptor은 다른 객체임!
*/
//블록 함수 사용
binding.list.apply {
layoutManager = LinearLayoutManager(applicationContext) //this는 recyclerAdapter를 가리킴
itemAnimator = DefaultItemAnimator()
setHasFixedSize(true) //에러나는 경우를 막기 위해 사용
adapter = songAdaptor
}
model.list.observe(this) {
//songAdaptor.notifyDataSetChanged() //warning 발생, 데이터가 바뀌면 뭐가 바뀐지는 모르기 때문에 아예 reset 시키고 전부 다 다시 보여주니까 비효율적임
songAdaptor.notifyItemMoved(0, model.list.value?.size ?: 0) //nullable인지 체크 해줘야 함, elvis 연산자 사용
}
for (i in 1..3) {
model.add("love me like that")
}
model.add("bye bye my blue")
}