Android 공부

Android 2way databiding - bindingAdapter(2)



-1. 이전글

2018/12/11 - [Android 공부] - 안드로이드 2way DataBinding



0. kotlin에서 databinding 사용하기

코틀린에서 데이터바인딩 사용하는 것은 예제만 따라하기에도 많은 시간이 사용된다.

gralde버전에 따라서 각종 변수가 달라지기도 하고, dataBinding에서 setVariable과 같은 매소드의 사용방법을

알려주는 사람이 없다거나, BindingAdapter와 같이 코드 또한 xml로 처리하고 싶을 때,

그리고 수많은 오류가 있기에 kotlin에서 databinding 사용하는 것은 나와 같이 초보 개발자들에게는

어려운 점이라고 생각한다.



.1. 지난 글


지난 글에서는 단순하게 @={변수명}을 통해서 xml을 통해서도 값을 넣을 수 있다는 것을 알 수 있었다.

이번에는 bind:glideImage="@{변수명}" 하나를 목표로 글을 쓰려고 한다.


2. 쉬운 것은 쉽다


BindingAdapter라는 개념은 아마 xml을 제대로 다루는 사람들에게는 낯이 익었을 것이라고 생각하지만,

나는 dataBinding을 하면서 처음봤다.


app:text, app:color와 같이 아무 생각없이 썼던 것들을 이제 나도 만들 수 있게 되었다.


2-1. JAVA 코드


1
2
3
4
@BindingAdapter(value = "rating")
fun RatingBar.doubleToInt(rating: Double) {
    this.rating = rating.toFloat()
}

cs


2-2. xml 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:bind="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="MovieVM"
            type="hbs.com.boostcampmoviesearch.MovieVM"/>
    </data>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="216dp">
 
            <RatingBar
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:clickable="false"
                bind:rating="@{MovieVM.movie.userRating}"/>
 
    </LinearLayout>
</layout>
 

cs



2-3. 해석:


사실 쉽지 않다.

java코드를 통해서 xml에 입히는 것은 쉬운게 없다고 생각한다.

예를 들면 CustomView를 만드는 것만 해도 참 복잡하다고 생각한다.


"@{MovieVM.movie.userRating}"


위의 MovieVM.movie.userRating은 MovieVM이라는 객체 아래의 movie객체 아래의 userRating이다.

만약에 MVVM패턴을 사용하고 있다면 MovieVM이라는 뷰모델에서 movie이라는 모델에 접근하는 것이다. 

그냥 간단히 하자면, userRating이라는 변수가 어떤 클래스에 있고 나는 이것을 MovieVM에 넣어놨다.


1
2
3
<variable
            name="MovieVM"
            type="hbs.com.boostcampmoviesearch.MovieVM"/>
cs


이것을 xml을 통해서 접근하기 위해서는 MovieVM이라는 클래스를 variable이라는 xml 태그를 통해서 설정해놔야한다.

그렇게 되면 우리는 해당 클래스를 xml에서 @{}를 통해서 접근할 수 있게 된다.


1
2
3
4
5
6
<RatingBar
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:clickable="false"
                bind:rating="@{MovieVM.movie.userRating}"/>
cs

bind:rating

이라는 친구는 무엇일까? 

우선, bind는 layout xmlns:bind="http://schemas.android.com/tools" 에서 온다.

그리고, rating이라는 것은 2-1. JAVA 코드에서 value에 있는 값이다.

이 조건을 완충시킨다면 RatingBar.rating에 접근해 값을 셋팅하는 것이다.

이처럼, 변수를 1개 셋팅하는 xml은 복잡하면서도 생각할게 없다. 

하지만 우리가 imageView를 사용하는데 Glide를 사용하고 이 Glide를 사용할 때 

메모리 절약하기 위해서 Activity로부터 requestManager를 유지하고 있다면 어떨까?


3. 기초가 튼튼해야 한다.


어노테이션도 Companion도 static이던, dataBinding.setVariable이던, 나는 아무것도 몰랐고 이제야 조금 알겠다.


3-0. 예제:


Glide의 requestManager의 객체를 유지하되

dataBinding의 BindingAdapter를 사용해서 image를 set해라.


3-1. Java 코드:


1
2
3
4
5
@JvmStatic
        @BindingAdapter(value = ["picture","requestManager"])
        fun ImageView.setPicture(picture: String, requestManager: RequestManager) {
            requestManager.load(picture).into(this)
        }
cs


3-2. xml 코드:


1
2
3
4
5
6
7
<ImageView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:scaleType="fitXY"
            bind:picture="@{MovieVM.movie.image}"
            bind:requestManager="@{requestManager}"/>
cs


3-3. 해석


@BindingAdapter는 해당 value들의 값들이 완충 된다면 자동으로 함수가 실행된다.

즉, BindingAdapter의 picture와 requestManager의 변수에 값이 다 들어온다면 해당 함수가 실행되는 것이다.

하지만, 모든 값을 넣지 않더라도 값이 실행되고 싶다고 한다면 requireAll이라는 변수를 false로 하면 된다.


다시 본론으로 돌아가서 Glide의 requestManager 객체를 유지한다면 

우리는 대부분 requestManager를 전역변수로 해서 참조 받을 것이다. 


하지만 그렇게 되면 


2018/12/12 - [Android Error] - java.lang.IllegalStateException: Required DataBindingComponent is null in class ItemMovieBindingImpl. A BindingAdapter in hbs.com.boostcampmoviesearch.MovieVM is not static and requires an object to use, retrieved from the DataBindingComponent. If you d..


해당 오류를 직면하고 말 것이다.

나는 이 오류가 무엇인지 몰랐고 stackoverflow에는 static으로 하라는 답만 있었다.

static으로 하게 되면 잘 처리될 지도 모른다.

하지만 정확한 정답은 xml을 통해서 변수값들을 받고 처리하는 것이다.


이 정답을 알기 전에 코틀린에서 static을 사용해보자.

그러면 Companion이 생각날 것이고 Companion 안에 함수를 넣게 되면 전역변수는 당연히

static형이 아니기 때문에 접근하지 못 하고 Companion만 독립적으로 존재하게 된다.

1
2
3
4
5
6
7
8
companion object {
        @JvmStatic
        @BindingAdapter(value = ["picture","requestManager"])
        fun ImageView.setPicture(picture: String, requestManager: RequestManager) {
            
            requestManager.load(picture).into(this)
        }
}

cs


이런 구조가 만들어지게 된다.

companion을 통해서 독립적으로 만들어지고 StackOverflow의 답변의 JvmStatic을 쓸 수 있는 환경이 되거나 한다.

하지만 가장 중요한 것은 requestManager를 전역변수로 사용하지 않았다는 것이 모범답안이 되는 경우였다.


4. xml에 변수 set하기


4-1. JAVA 코드 :

1
binding.setVariable(BR.requestManager, requestManager)
cs

4-2. xml 코드 :

1
2
3
<variable
            name="requestManager"
            type="com.bumptech.glide.RequestManager"/>
cs

4-3. 해석 :


우리가 DataBindingUtil.setContentView 등을 통해서 binding이라는 객체를 획득하게 된다면

xml의 variable에 값을 set 하기 위해서는 binding.setVariable(name, 객체) 를 통해서 접근해야한다는 것을 알 수 있다.

 


5. 결론.


@BindingAdapter는 전역변수를 사용하면 안 되는 독립적인 구조가 만들어져야한다.

'Android 공부' 카테고리의 다른 글

안드로이드 Room 고군분투  (0) 2018.12.21
Retrofit을 통한 Naver 영화 API 사용하기  (0) 2018.12.17
안드로이드 2way DataBinding  (0) 2018.12.11
LiveData의 간단한 예제  (0) 2018.11.30
android.arch.lifecycle  (0) 2018.11.30