[개념] 데이터 바인딩(data Binding) (5)

쓰리원·2022년 5월 11일
0

binding 정리

목록 보기
6/6
post-thumbnail

binding 글 링크

개념-데이터-바인딩data-Binding (1)
개념-데이터-바인딩data-Binding (2)
개념-데이터-바인딩data-Binding (3)
개념-데이터-바인딩data-Binding (4)
개념-데이터-바인딩data-Binding (5)

1. data binding 라이브러리 사용

1. Binding adapters

바인딩 어댑터는 값을 설정하기 위해 적절한 프레임워크 호출을 수행해야 합니다. 한 가지 예는 setText()메서드 호출과 같은 속성 값을 설정하는 것입니다. 또 다른 예는 setOnClickListener()메서드 호출과 같은 이벤트 리스너를 설정하는 것입니다. 데이터 바인딩 라이브러리를 사용하면 값을 설정하기 위해 호출된 메서드를 지정하고, 고유한 바인딩 논리를 제공하고, 어댑터를 사용하여 반환된 개체의 유형을 지정할 수 있습니다.

즉, 데이터가 업데이트 될 때마다, 자동으로 생성된 Binding 클래스는 View를 업데이트하기 위해 각 특성(XML Attribute)의 setter 메서드를 호출합니다 이때 자동으로 setter 메서드를 호출하거나, 개발자가 커스텀하게 setter메서드를 호출하게 할 수 있습니다.

2. Setting attribute values

바인딩된 값이 변경될 때마다 생성된 바인딩 클래스는 바인딩 식을 사용하여 뷰에서 setter 메서드를 호출해야 합니다. 데이터 바인딩 라이브러리가 자동으로 메서드를 결정하도록 하거나 메서드를 명시적으로 선언하거나 메서드를 선택하기 위한 사용자 지정 논리를 제공할 수 있습니다.

1. Automatic method selection

layout 속성에는 여러 종류가 있습니다. TextView 를 예로 들면 android:textColor, android:text 등등 입니다. 데이터 바인딩을 사용하면 바인딩 식이 적용 된 xml 속성에 대해 자동으로 setter 메소드를 찾아서 선택하고, 호출 합니다. 속성의 네임스페이스는 고려되지 않으며 메서드 검색 시 속성 이름 및 유형만 사용됩니다.

예를 들어 android:text="@{user.name}" 표현식이 있는 경우 라이브러리는 user.getName()에서 반환한 유형을 허용하는 setText(arg) 메서드를 찾습니다. user.getName()의 반환 유형이 String이면 라이브러리는 String 인수를 허용하는 setText() 메서드를 찾습니다. 표현식이 int를 대신 반환하면 라이브러리는 int 인수를 허용하는 setText() 메서드를 검색합니다. 표현식은 올바른 유형을 반환해야 합니다. 필요하다면 반환 값을 변환할 수 있습니다.

만약 android:text 속성에 바인딩 식이 적용 되어있다면 TextView 의 setText() 를 찾아서 binding 됩니다.

또한 별도의 xml 속성을 제공하지 않는 특정 setter 메소드와 바인딩 하여 사용 할 수 있습니다. DrawerLayout 을 예로 들어 보겠습니다. DrawerLayout 의 경우에는 해당 위젯에 대한 어떠한 XML attribute 도 제공하지 않습니다. 하지만 다양한 setter 메소드를 제공 하고 습니다. 여기서 setScrimColor(int color), setDrawerListener(DrawerLayout.DrawerListener listener) 이 두개의 setter 메소드로 예제를 살펴보겠습니다.

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

app:scrimColor 와 app:drawerListener는 기본으로 제공하는 XML 속성이 아니지만, 데이터바인딩 라이브러리를 사용하면 scrimColor 와 drawerListener 에 대한 setter 메소드를 찾아서 자동으로 적용 하게 됩니다.

그러나 setter 와 속성 이름이 일치하지 않는 경우가 있을 것입니다. android:tint 의 경우 매핑 되는 setter 메소드는 setTint() 가 아닌 setImageTintList() 입니다. 이런 경우에는 BindingMethod annotation을 사용하여 속성과 setter를 연결 할 수 있습니다.

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

대부분의 경우 Android 프레임워크 클래스에서 setter의 이름을 변경할 필요가 없습니다. 속성은 일치하는 메서드를 자동으로 찾기 위해 이름 규칙을 사용하여 이미 구현되었습니다.

2. Provide custom logic

일부 속성에는 맞춤 결합 로직이 필요할 수 있습니다. 예를 들어 android:paddingLeft 속성에는 연결된 특정 setter 메서드가 없습니다. 대신 setPadding() 이라는 setter 가 있으며 이 메서드를 이용하여 BindingAdapter 주석이 있는 정적 결합 어댑터 메서드를 사용하면 커스텀 바인딩 메서드를 맞춤 설정해서 XML 속성과 연결 할 수 있습니다.

예를 들어 아래는 paddingLeft 속성의 결합 어댑터를 보여줍니다.

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

setPadding(int left, int top, int right, int bottom) 메서드를 이용해서 커스텀 메서드가 구현 된 예시입니다. 커스텀 바인딩 로직 구현을 위해서는 BindingAdapter annotation을 사용합니다. 괄호안에는 연결시키고자 하는 xml 속성 이름이 들어갑니다.

setPaddingLeft의 매개변수 유형의 첫 번째는 속성과 연결된 뷰의 유형을 결정합니다. 두 번째는 지정된 속성의 결합 표현식에서 허용되는 유형을 결정합니다. 결합 어댑터는 다른 유형의 맞춤설정에 유용합니다. 예를 들어 맞춤 로더는 작업자 스레드에서 호출되어 이미지를 로드할 수 있습니다. 개발자가 정의하는 결합 어댑터는 충돌이 발생하면 Android 프레임워크에서 제공하는 기본 어댑터보다 우선 적용됩니다.

또한 여러개의 속성과 연결 시킬 수도 있습니다. 이번에는 직접 커스텀 바인딩 메서드를 구현해보겠습니다.

class MyBindingAdapter {
    companion object {
        @JvmStatic
        @BindingAdapter("imageUrl", "error")
        fun loadImage(view: ImageView, url: String, errorDrawable: Drawable) {
            Picasso.with(view.getContext()).load(url).error(errorDrawable).into(view);
        }
    }
}
  • BindingAdapter 메서드는 접근자가 public 이고 static 메서드로 작성해야 합니다. 따라서 kotlin 에서 사용하기 위해 @JvmStatic 을 추가로 정의합니다.

  • @BindingAdapter annotation 을 작성 합니다. 여기서는 "imageUrl", "error" 속성과 매핑 하였습니다. "bind:imageUrl" 과 같이 특정 namespace 로 작성 할 수 있으나 xml 에서 꼭 namespace를 동일하게 사용할 필요는 없습니다.

  • 여러개의 속성을 적용 할때는 XML 에서 해당 속성들을 모두 사용해야 해당 BindingAdapter를 사용 할 수 있습니다. 위의 코드로 예를들면 xml 에서 imageUrl 속성은 사용하고 error 속성은 사용하지 않으면 해당 adapter 를 사용 할 수 없습니다.

  • 메서드의 매개변수는 중요합니다. 첫번째 인자는 반드시 적용하려는 view 를 매개변수로 해야 합니다. 나머지 인자는 BindingAdapter annotation 에 정의한 속성에 들어갈 타입에 맞춰서 순서대로 작성 되어야 합니다. annotation 의 속성을 imageUrl (String) error (Drawable) 순서로 작성 하였기 때문에 메서드의 두번째 , 세번째 매개변수의 타입이 각각 String 과Drawable 로 작성 되었습니다

XML 에서는 아래와 같이 사용하면 됩니다.

<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />

3. 예제 코드

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        val item = User(imageview = "https://picsum.photos/200", name = "배경화면")
        binding.user = item
    }
}

class MyBindingAdapter {
    companion object {
        @JvmStatic
        @BindingAdapter("imageUrl")
        fun loadImage(imageView : ImageView, url : String) {
            Glide.with(imageView.context)
                .load(url)
                .error(R.drawable.close_24)
                .into(imageView)
        }
    }
}

data class User(var imageview : String,
                var name : String)   
<?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="user"
            type="com.project.bindingadapter.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="@{user.name}"
            android:textSize="50sp"
            tools:text="name" />

        <ImageView
            android:layout_width="160dp"
            android:layout_height="160dp"
            app:imageUrl="@{user.imageview}" />

    </LinearLayout>
</layout>

구현 코드 : https://github.com/ilil1/BindingAdapter

2. reference

https://developer.android.com/topic/libraries/data-binding/start
https://developer.android.com/topic/libraries/data-binding
https://developer.android.com/topic/libraries/data-binding/expressions
https://developer.android.com/topic/libraries/data-binding/observability
https://developer.android.com/topic/libraries/data-binding/binding-adapters

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글