Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

기록장

[코틀린] LiveData, BindingAdapter 사용해보기 (with ViewModel and DataBinding) 본문

코틀린

[코틀린] LiveData, BindingAdapter 사용해보기 (with ViewModel and DataBinding)

edit0 2021. 3. 27. 00:21

바로 이전 포스팅에서는 데이터바인딩과 뷰모델에 대해서 알아보았습니다.

라이브데이터가 나오긴 했었지만 거의 설명이 없었습니다. 그래서 이번에는 데이터바인딩, 뷰모델과 함께 라이브데이터와 바인딩어댑터를 알아보도록 하겠습니다.

 

라이브데이터는 데이터가 변하게 되었을 경우를 감지하여 콜백해주는 특징을 가지고 있습니다.

뭔가 변화를 감지해서 나에게 알려준다니 UI 업데이트를 할 경우 편할 것 같은 느낌이 듭니다.

 

라이브데이터의 몇가지 장점을 알아보도록 하겠습니다.

1. 사용하고 있는 액티비티의 Lifecycle을 알고 있기 때문에 메모리 누수를 방지할 수 있습니다.

2. UI 업데이트를 바로바로 해줄 수 있기 때문에 최신 데이터를 바로 반영할 수 있습니다.

3. 데이터 변화에 따라 Observer 객체에 알려주기 때문에 그에 맞는 코드를 작성할 수 있습니다. 

 

여기까지 라이브데이터에 대한 간략한 설명이었습니다.

이어서  함께 알아볼 바인딩어댑터는 코드를 함께 보며 설명 드리도록 하겠습니다.

(바인딩어댑터는 커스텀 속성을 만들어주는 것 입니다.)

 


시작 전 셋팅부터 해줍니다.

 

그래들.app 파일

android {
   ...

    dataBinding {
        enabled true
    }
    
    ...
}

dependencies {
    ...
    
    implementation 'androidx.activity:activity-ktx:1.1.0'
}

 

아래 코드 XML의 루트가 왜 <layout>이고 <data>, <variable>을 왜 사용하는지 잘 모르시겠다면 또는 데이터바인딩, 뷰모델을 처음 접하신다면 먼저 보시고 오시면 이해하시는데 도움이 될 것 같습니다.

edit0.tistory.com/46

 

[코틀린] ViewModel, Databinding 사용해보기 (with LiveData)

Databinding은 말그대로 데이터를 묶는 의미로, XML(UI)과 데이터(ViewModel 내)를 결합하여 사용할 수 있도록 도와주는 라이브러리입니다. 물론 UI를 담당하는 Java나 Kotlin 소스 코드에서 데이터나 UI 업

edit0.tistory.com

 

에디트텍스트와 버튼, 텍스트뷰에는 각각에 대입되는 표현식을 가지고 있습니다.

 

에디트텍스트의 @={ .. } 는 { } 안에 선언된 뷰모델 변수에 값을 넣어주겠다는 의미

버튼의 @{ ( ) -> .. } 은 뷰모델의 메소드를 호출

텍스트뷰의 @{ .. } 는 안에 선언된 변수를 Text에 할당하겠다는 의미

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout
    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">

    <data>
        <variable
            name="viewModel"
            type="com.kotlin.k_bindingadapter.Main_ViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"
        android:orientation="vertical"
        android:gravity="center_horizontal">

        <EditText
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:id="@+id/et1"
            android:text="@={viewModel.changeName}"/>

        <Button
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:text="이름 바꾸기"
            android:onClick="@{() -> viewModel.ButtonOnClick()}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/name"
            android:textSize="30dp"
            android:text="@{viewModel.name}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/update_name"
            android:textSize="20dp"
            android:text="@{viewModel.updateName}"/>
        
    </LinearLayout>
    
</layout>

 

뷰모델 클래스입니다.

MutableLiveData 형으로 선언된 name은 라이브데이터를 사용할 수 있는 타입이라고 보시면 됩니다.

(감시당할 수 있는 데이터라고 생각하시면 쉽습니다.)

 

Main_ViewModel.kt

class Main_ViewModel : ViewModel() {

    var name = MutableLiveData<String>()
    var changeName = ObservableField<String>()
    var updateName = ObservableField<String>()

    fun ButtonOnClick(){
        name.value = changeName.get()
    }
}

 

ViewModelProvider를 통해 뷰모델을 생성하고 바인딩 객체 설정을 기본적으로 해줍니다.

여기서 중요한 것은 Observer<String> 입니다. 말그대로 String 타입 변수의 변화를 감지합니다. 감시할 변수를 observe()로 등록해주면 이 변수의 변화가 일어날 때마다 콜백이 됩니다. 

 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    var viewModel = Main_ViewModel()
    lateinit var binding:ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelProvider(this).get(Main_ViewModel::class.java)

        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        binding.setLifecycleOwner(this)
        binding.viewModel = viewModel


        var thisname = Observer<String> { result ->
            Toast.makeText(binding.root.context, "이름이 변경되었습니다. / Result: $result", Toast.LENGTH_SHORT).show()
            viewModel.updateName.set("라이브데이터 UI 업데이트: $result")
        }
        binding.viewModel?.name?.observe(binding.lifecycleOwner?:this, thisname)
    }
}

 

결과

 

 


이어서 바인딩어댑터 코드를 보도록 하겠습니다.

 

데이터바인딩을 할 경우 이미지를 불러와야 할 상황이 있을 수 있습니다.

다른 데이터들은 뷰모델의 변수를 XML에 선언하여 업데이트를 해줄 수 있지만 이미지는 그렇지 않습니다.

 

시작 전에 인터넷 퍼미션을 꼭 추가해주시길 바랍니다.

 

아래에 사용할 이미지 출처: www.themoviedb.org/?language=ko

 

위에서 사용했던 activity_main.xml 파일에 이미지뷰를 추가해줍니다.

<ImageView
  android:layout_width="150dp"
  android:layout_height="150dp"
  android:scaleType="fitXY"
  setImage="@{viewModel.image_path}"
  Error="@{@drawable/ic_launcher_foreground}"/>

 

Glide 라이브러리 추가 (이미지 출력을 위해)

implementation 'com.github.bumptech.glide:glide:4.11.0'

 

Main_ViewModel.kt 파일에 아래 코드를 추가해줍니다.

var image_path = ObservableField<String>()

  init {
  	image_path.set("8xWV5umarehP0WvfZr9rmFE9wSl.jpg")
  }

이 주소는 TMDB 오픈 API에서 제공하는 영화 URL 주소입니다.

 

새롭게 파일 하나를 Object 형식으로 만들어줍니다.

object BindingAdapter {

    @BindingAdapter("setImage", "Error")
    @JvmStatic
    fun a(iv: ImageView, url: String, error: Drawable ){
        var baseurl = "http://image.tmdb.org/t/p/original/"
        Glide.with(iv.context).load(baseurl + url).override(500,500).error(error).into(iv)
    }

}

"setImage"와 "Error"는 XML에서 사용할 속성 이름들이 되겠습니다.

코틀린은 static을 지원하지 않기 때문에 어노테이션을 추가해줍니다.

메소드를 정의합니다. 첫 번째 파라미터는 ImageView(이미지뷰에서 사용할 예정이므로) 두 번째는 함께 보낼 url, 세 번째는 서버에서 불러온 이미지가 에러가 날 경우 대체할 이미지를 Drawable 형태로 보내줍니다.

그 후 Glide 라이브러리를 통해 이미지를 출력해줍니다.

 

결과

 

정상적으로 이미지 출력 시

 

이미지 url 에러 시

 

라이브데이터와 바인딩어댑터에 대해 간단히 알아보았습니다.

 

감사합니다.