Jackpack_LiveData

소정·2023년 6월 2일
0

Kotlin

목록 보기
22/27

💡 LiveData

  • ObservaleXXX 와 다르게 라이프사아클에 따라 효율적으로 UI갱신
  • LiveData는 추상클래스임 MutableLiveData를 써야함

[0] 화면 UI

<?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>



[1] 기본 사용

메인

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()
}

뷰모델

  • MutableLiveData 변수 타입 지정 해서 UI갱신에 반영 할 수 있는 변수로 만들어준다
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)


}



[2] 버튼으로 값 바꾸기

메인

  • 버튼 클릭해서 값 바꿔 하면 LiveData는 안바뀜 바로 반영 안됨 변수를 관찰하는 observe() 메소드를 통해 관찰 및 UI갱신

💡 observe(LifecycleOwner , 메소드 동작)
LifecycleOwner = 오너의 종류는 액티비티 or 프래그먼트
-> 현재 페이지와 생명주기를 같이하기 위해

☝ 자동 갱신 2가지 방법

  1. LiveData 변수를 관찰하는 observe() 메소드를 통해 관찰 및 UI갱신
  • 메소드에 각각 설정
  1. 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
    }


}



[3] 리사이클러뷰

  1. 아이템 UI이와 그 안에 보여줄 item data class 준비 후 데이타 바인딩으로 연결
  2. 리사이클러 아답터 준비
  3. 메인화면에 리사이클러뷰와 연동
  4. 메인화면과 연동해야하는데...데이터를 속성으로 지정 해야하는데 데이타 속성은 없다 내가 만들어야함
  5. @BindingAdapter로 새 속성 만들어서 메인화면에 만든 속성으로 연동

itemUI

<?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>

item data class

package com.bsj0420.ex99jackpacklivedataa

data class Item(
    var title:String,
    var msg:String,

)

리사이클러뷰 item adapter

  • 매개변수로 받던 Context는 사실 받을 필요 없었음 모든 View는 context를 가지고 있음
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) //별도 스레드로 설정 작업! - 셋벨류 작업이 오래 걸리면... 이걸로
    }


}

@BindingAdapter

  • 속성으로 데이타 연동하는 것은 없음 내가 만들어야함
  • 리사이클러뷰에서 리스트 데이터를 설정하는 새로운 속성 만들기
  • static 메소드만 가져야함
  1. 싱글턴으로 만들기 위해 object
  2. static 메소드여야해서 @JvmStatic 추가
  3. 코틀린에서 어노테이션을 쓰려면 빌드그래이들에 어노테이션 쓴다고 말해야됨
  4. adapter연동하는 함수 작성!!
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>)

    }

}

위 작업 후 메인과 연동


[4] 데이터바인딩 사용주의


MutableLiveData 를 쓸 때 초기화 안하면 nullpointException 난다


🧨 .과 :: 의 차이

xml 에서 뷰모델에 만든 함수에 접근 할 때 두개의 차이

:: 은 뷰모델에 써있는 함수를 지정
. 은 함수 호출!! 함수를 호출할 땐 익명함수로 호출해야한다

profile
보조기억장치

0개의 댓글