💡 LiveData
- ObservaleXXX 와 다르게 라이프사아클에 따라 효율적으로 UI갱신
- LiveData는 추상클래스임 MutableLiveData를 써야함
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="vm"
type="com.bsj0420.ex99jackpacklivedataa.MyViewModel" />
</data>
<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="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_name"
android:textColor="@color/black"
android:textSize="25sp"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.name}"
/>
<TextView
android:textColor="@color/black"
android:textSize="25sp"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(vm.age)}"
/>
<!-- 버튼 클릭으로 데이터 변경 및 화면자동 갱신 -->
<Button
android:backgroundTint="#E91E63"
android:text="체인지 네임"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{(v)->vm.changeName()}"/>
<Button
android:backgroundTint="#C7BADD"
android:text="나이 증가"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{(v)->vm.increaseAge()}"/>
<!-- 리사이클러뷰 실습!!! - 데이터를 속성으로 지정해야 하지만
데이타 연동은 없음 새로운 속성을 만들어야함...@BindingAdapter문법!!!!
-->
<androidx.recyclerview.widget.RecyclerView
app:itemList="@{vm.items}"
android:layout_width="match_parent"
android:layout_height="300dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:orientation="vertical"/>
<!-- 아이템 추가 버튼 -->
<Button
android:backgroundTint="#03A9F4"
android:text="아이템 추가"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{(v)->vm.addItem()}"/>
</LinearLayout>
</layout>
package com.bsj0420.ex99jackpacklivedataa
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import com.bsj0420.ex99jackpacklivedataa.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding:ActivityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
//뷰모델 객체와 연결
binding.vm = MyViewModel()
}
package com.bsj0420.ex99jackpacklivedataa
import androidx.lifecycle.MutableLiveData
class MyViewModel {
//LiveData : ObservaleXXX 와 다르게 라이프사아클에 따라 효율적으로 UI갱신
//LiveData는 추상클래스임 MutableLiveData를 써야함
val name : MutableLiveData<String> = MutableLiveData("sam")
val age : MutableLiveData<Int> = MutableLiveData(20)
}
💡 observe(LifecycleOwner , 메소드 동작)
LifecycleOwner = 오너의 종류는 액티비티 or 프래그먼트
-> 현재 페이지와 생명주기를 같이하기 위해
☝ 자동 갱신 2가지 방법
- LiveData 변수를 관찰하는 observe() 메소드를 통해 관찰 및 UI갱신
- 메소드에 각각 설정
- LiveData의 변화를 반영할 LifecycleOwner를 미리 지정
- binding 전체에 LifecycleOwner 지정
package com.bsj0420.ex99jackpacklivedataa
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import com.bsj0420.ex99jackpacklivedataa.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding:ActivityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)
//뷰모델 객체
// binding.vm = MyViewModel()
val vm : MyViewModel = MyViewModel()
binding.vm = vm
//자동갱신 2가지
//1. LiveData 변수를 관찰하는 observe() 메소드를 통해 관찰 및 UI갱신
//뷰 모델 안에 있는 LiveData 변수를 관찰하는 설정
//오너의 종류는 액티비티 or 프래그먼트 : 생명주가를 같이하기 위해
// vm.name.observe(this, object : Observer<String>{
// override fun onChanged(t: String?) {
// //이렇게 object 익명객체를 사용하는 것은 너무 길어 짱남
// }
// })
//샘변환 할 때 파라미터가 두개면 메소드로 만들것은 {} 아닌애는 ()로 나눠서 쓸 수 있음
vm.name.observe(this){
Toast.makeText(this, "데이터 변경 감지 - $it", Toast.LENGTH_SHORT).show()
//id 주고 바꾸라고 말 해줘야함
binding.tvName.text = it
}
//2. LiveData의 변화를 반영할 LifecycleOwner를 미리 지정
//자동갱신 2번째 방법 [대부분 이방식]
binding.lifecycleOwner = this
}
}
package com.bsj0420.ex99jackpacklivedataa
import androidx.lifecycle.MutableLiveData
class MyViewModel {
//LiveData : ObservaleXXX 와 다르게 라이프사아클에 따라 효율적으로 UI갱신
//LiveData는 추상클래스임 MutableLiveData를 써야함
val name : MutableLiveData<String> = MutableLiveData("sam")
val age : MutableLiveData<Int> = MutableLiveData(20)
//버튼 클릭 이벤트 콜백 메소드
fun changeName() {
name.value = "Lisa"
//LiveData는 ObservaleXXX와 다르게 값을 변경해도 자동 갱신하지 않는다
//자동 갱신 2가지 방법
//1. LiveData 변수를 관찰하는 observe() 메소드를 통해 관찰 및 UI갱신 => mainActivity.tk에 사용방법 있음
//2. LiveData의 변화를 반영할 LifecycleOwner를 미리 지정 - binding 전체에 LifecycleOwner 지정
}
fun increaseAge() {
age.value = age.value!!.toInt() + 1
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="item"
type="com.bsj0420.ex99jackpacklivedataa.Item" />
</data>
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
app:strokeWidth="0.5dp"
app:strokeColor="@color/black"
app:contentPadding="16dp"
android:layout_margin="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@{item.title}"
android:textSize="23sp"
android:padding="4dp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@{item.msg}"
android:textSize="25sp"
android:padding="4dp"
android:textStyle="italic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
package com.bsj0420.ex99jackpacklivedataa
data class Item(
var title:String,
var msg:String,
)
package com.bsj0420.ex99jackpacklivedataa
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bsj0420.ex99jackpacklivedataa.databinding.RecyclerItemBinding
//실수로 값 바꿀 까봐 List로 받음
class RecyclerItemAdaper(val items:List<Item>) : Adapter<RecyclerItemAdaper.VH>() {
inner class VH(val binding: RecyclerItemBinding) : ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
//뷰에는 콘텍스트가 있으니까 parent한테 받아옴
val binding:RecyclerItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context),R.layout.recycler_item,parent,false)
return VH(binding)
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: VH, position: Int) {
holder.binding.item = items[position] // 포지션 번째 넣어 아이템아~
}
}
리사이클러뷰에 LiveData 변수 적용
package com.bsj0420.ex99jackpacklivedataa
import androidx.lifecycle.MutableLiveData
class MyViewModel {
// 2. 리사이클러뷰에 LiveData 변수 적용
val items : MutableLiveData<MutableList<Item>> = MutableLiveData(mutableListOf(Item("lisa","sexy")))
//2.1 ) 아이템 추가하면 화면 자동 갱신
fun addItem() {
val list = items.value //items에 add따위는 없으니까 list에 넣어서 하기
list?.add(0, Item("New", "dddd")) //새로운 거 0번지에 넣어
// items.value = list
items.postValue(list) //별도 스레드로 설정 작업! - 셋벨류 작업이 오래 걸리면... 이걸로
}
}
package com.bsj0420.ex99jackpacklivedataa
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
//싱글턴으로 만들기 위해 object
object MyBindingAdapter {
//리사이클러뷰에서 리스트 데이터를 설정하는 새로운 속성 만들기 [속성명 : itemList]
@BindingAdapter("itemList")
//To use data binding annotations in Kotlin, apply the 'kotlin-kapt' plugin in your module's build.gradle
//빌드 추가
@JvmStatic
fun setItemList(view:RecyclerView, items:Any) { //컬랙션은 Any로 받는다
view.adapter = RecyclerItemAdaper(items as List<Item>)
}
}
MutableLiveData 를 쓸 때 초기화 안하면 nullpointException 난다
🧨 .과 :: 의 차이
xml 에서 뷰모델에 만든 함수에 접근 할 때 두개의 차이
:: 은 뷰모델에 써있는 함수를 지정
. 은 함수 호출!! 함수를 호출할 땐 익명함수로 호출해야한다