본문 바로가기

[Android] DataBinding #3 - Event, BindingAdapter, Conversion

DataBinding #3 - Event, BindingAdapter

 

DataBinding 이전 포스트

1. DataBinding #1 - 기본

2. DataBinding #2 - Observable Object/Field/Collection


 

INDEX

 

 

Event

  • View에서 발생하는 이벤트 처리방법 (기본 Basic / 데이터바인딩 DataBinding)
  • Basic Event Logic / Data Binding Event Logic 비교

 

Basic Event Logic - 기본 방법

: View의 evnet 처리방법에는 익명 클래스 or 인터페이스 구현(Implements) 등 여러 방법이 존재

// 사용방법 1 - 람다식
button.setOnClickListener(view -> Log.d(TAG, "Button Click!"));

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

// 사용방법 2 - 익명 클래스
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    	Log.d(TAG, "Button Click!");
    }
});

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

// 사용방법 3 - 인터페이스 구현 (implements)

public class MainActivity implements View.OnClickListenr {
    ...
    button.setOnClickListener(this);

    ...
    @Override
    public void onClick(view v) {
        Log.d(TAG, "Button Click!");
    }
}

 

Data Binding Event Logic - 데이터 바인딩 방법

: 데이터 바인딩을 이용한 방법에는 메서드 참조 / 리스너 바인딩 두 가지 방법 

 

  • 메서드 참조

: 클래스에 선언된 메서드를 데이터 바인딩을 통해 xml에서 직접 바인딩이 가능


: 메서드 참조방식, View.onClick() 메서드와 매개변수/리턴 타입이 모두 동일해야 합니다

 

  • 리스너 바인딩

: 메서드 참조와 비슷하지만, 리스너 바인딩은 임의의 데이터 바인딩 식을 이용할 수 있습니다


: 리스너 바인딩은 메서드 참조와 달리 리턴타입만 일치시키면 됩니다

 

리스너 바인딩 방식과 메서드 참조 방식은 임의의 데이터 바인딩식을 이용할 수 있다는 점이 차이점

  • 메서드 참조

: 매개변수, 리턴 타입 모두 일치

데이터가 바인딩될 때 실제 리스너 구현이 생성

  • 리스너 바인딩

: 리턴 타입만 일치

이벤트 발생할 때 실제 리스너 구현이 생성

 

 

 

BindingAdapter

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

 

Basic Attribute - 기본 특성

: View의 기본 xml 특성(Attribute)의 경우, 데이터 바인딩은 자동으로 해당 특성의 setter 메서드 호출

Textview Attribute / setter Method

: TextView의 android:text 특성setText(int, TextView.BufferType)setter 메서드를 호출합니다

 

Custome Attribute - 사용자 지정 Setter

: View의 기본 특성(Attribute)이 아닌 사용자 지정(Custom) 특성을 이용하는 방법

  • Custom 특성(메서드) 생성

: public static 특성(메서드) 작성 

@BindingAdapteR("특성이름") 애노테이션(주석) 추가

public class BindingAdapter {
	// @BindingAdapter("visible") - 'visible'이 사용자지정 특성이름
    @androidx.databinding.BindingAdapter("visible")
    public static void setVisible(View view, boolean isVisible){	// 파라미터 2개 (view, boolean값)
        // UI Update 로직
        if (isVisible) {
            view.setVisibility(View.VISIBLE);
        } else {
            view.setVisibility(View.GONE);
        }
    }
}

 

  • xml 레이아웃 파일에서 View에 사용자 지정 특성 추가

: @BindingAdapter("특성이름") 에 썻던 특성이름으로 추가

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TEST View"
    app:visible="@{isVisible}" />	// 사용자지정 특성(app:visible)

 

여러 매개변수를 받는 BindingAdapter

View를 제외한 2개의 파라미터를 받는 loadImage 메서드

@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
   Picasso.with(view.getContext()).load(url).error(error).into(view);
}

 

<!-- loadImage Setter 메서드 호출 -->
<ImageView 
    ...
    app:imageUrl="@{venue.imageUrl}"
    app:error="@{@drawable/venueError}"/>
    
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
<!-- loadImage Setter 메서드 호출불가 (error 속성이 없음) -->
<ImageView 
    ...
    app:imageUrl="@{venue.imageUrl}" />
    
    
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
<!-- loadImage Setter 메서드 호출불가 (ImageView가 아님) -->
<TextView 
    ...
    app:imageUrl="@{venue.imageUrl}" />
    

 

  • 위의 loadImage 메서드는 @BindingAdapter()에 나열된 2개의 특성(imageUri, error)

    모두 포함하는 view에서만 호출됩니다

  • loadImage 메서드는 3가지 호출조건을 만족해야 호출됩니다


    1) imageUrierror가 둘 다 ImageView에 사용되고


    2) app:imageUri 속성값이 문자열(String)이며

    3) app:error 속성값이 Drawable일 경우

 

 

 

 

One-way / Two-way binding

  • 데이터의 흐름의 방향을 의미
  • One-way binding
      : 단방향, 웹cafe 최신글의 리스트를 받아와서 화면에 보여준다면 데이터 흐름은 Code->View의 단방향
  • Two-way binding
      : 양방향, 웹cafe 게시글 중 "음식" 단어를 포함한 게시글을 검색한다면,

        데이터의 흐름은 Code->View, View->Code의 양방향을 띄게 됩니다

 

 

 

 

Converter - 변환기

 

Object (객체) 변환

: xml의 바인딩식 "@{...}"에서 Object가 반환되면 자동Setter / 이름이 바뀐Setter / 맞춤Setter 중에서
하나를 골라 메서드를 실행하게 됩니다

이러한 방식은 ObservableMap을 사용해서 데이터를 받아오는 경우에 편리한 변환방법입니다

- MainActivity.java

ObservableArrayMap userMap = new ObservableArrayMap<String, Object>();
userMap.put("name","홍길동");
userMap.put("age",20);
        
binding.setUserMap(userMap);


- activity_main.xml

<data>
    <import type="androidx.databinding.ObservableMap"/>
    <variable
    	name="userMap"
        type="ObservableMap&lt;String, Object&gt;" />
</data>

    <!-- key=name의 Object는 String -> CharSequence 캐스팅 -->
    <TextView
    	...
        android:text='@{userMap["name"]}' /> 
        
    <!-- Error, key=age의 Object는 int -> CharSequence 캐스팅 불가 -->
    <TextView
    	...
        android:text='@{userMap["age"]}' />


: userMap은 Object를 반환하는 Map컬렉션입니다. xml에서 바인딩식 "@{...}"으로 Object가 반환되면,

android:text 특성의 자동 setter메서드인 setText(CharSequence c) 메서드를 실행합니다.

반환된 Object는 setText()의 파라미터 타입으로 자동 캐스팅(형변환)을 하는데 위 예시에선,

key=name의 Object는 String 타입으로 CharSequence로 형변환이 가능하지만

key=age의 Object는 int 타입으로 CharSequence로 형변환이 불가하여 예외가 발생합니다 

<!-- String.valueOf(int) 함수로 강제 형변환 (int -> String) -->
<TextView
    ...
    android:text='@{String.valueOf(userMap["age"])}' />

: 바인딩식의 반환 Object 형식에 불안하다면 이렇게 개발자가 미리 식을 통해 형변환을 하여 예외를 방지해야 함

 

 

사용자 지정 변환 (Custom Conversion)

: 특정 형식 간에 자동으로 변환이 필요할 때가 있습니다. 예를 들어 background 배경설정을 보겠습니다

 

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>


: android:background 특성의 setter함수는 setBackground(ColorDrawable drawable)의 형식입니다.

위 예시에서 바인딩식의 반환값은 @color/red 또는 @color/white의 int형 color값을 반환합니다.

ColorDrawable 타입의 매개변수에 int형 color값을 넣으면 예외를 일으키게 되므로,

Drawable 속성값이 들어가야할 특성에 int형이 들어오는 경우엔 int -> Drawable로 자동 변환이 필요합니다

@BindingConversion 애노테이션과 함께 정적 메서드를 사용하면 해결이 가능

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
   return new ColorDrawable(color);
}

: converterColorToDrawable() 메서드는 @BindingConversion 주석을 통해 변환기 함수라는 걸 알려줍니다

@BindingAdapter처럼 특성이름도 없는데 어떻게 이 메서드를 호출하는건지 이것만 보면 이해가 가질않습니다

 

호출원리는 xml의 특성값이 ColorDrawable타입이여야 하는데 int형의 값이 들어왔을 경우

@BindingConversion 주석이 달린 메서드들 중에서 반환값이 ColorDrawable이고,
입력값이 int형인 메서드를 찾아서 실행해줍니다

<View
   android:background='@{isError ? "red" : "white"}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>


: 만약 위 예시처럼 이번엔 ColorDrawable이 들어와야하는데 String값이 들어온 경우엔 아래처럼 

String 파라미터를 받고 ColorDrawable을 반환하는 Conversion 메서드를 선언해줘야 합니다 

@BindingConversion
public static ColorDrawable convertColorToDrawable(String color) {
   return new ColorDrawable(color);
}

 

 

 

Conversion 메서드 호출 조건

  • 특성의 setter호출 레벨에서 setter 파라미터 타입과 입력값이 다를 때
  • @BindingConversion 주석이 달린 메서드들 탐색
  • @BindingConversion 메서드 파라미터 타입이 입력값과 같고
  • @BindingConversion 메서드 반환타입이 특성 setter메서드 파라미터타입과 같은경우