[TIL]221123 - ListViewModel

Jimin·2022년 11월 29일
0
post-thumbnail

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")

    }

0개의 댓글