ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Architecture Components #1 - DataBinding
    Android/AAC 2019. 7. 24. 00:07

    우리가 흔히 개발하는 View 코드를 아래에서 살펴보도록 하자

    class MainActivity : AppCompatActivity() {
    
        private lateinit var textName: TextView
        private lateinit var textAge: TextView
        private lateinit var textEmail: TextView
        private lateinit var textPhone: TextView
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            textName = main_view_name
            textAge = main_view_age
            textEmail = main_view_email
            textPhone = main_view_phone
    
            val user = User("홍길동", 20, "010-0000-0000", "gildong02.hong@samsung.com")
    
            textName.text = user.name
            textAge.text = user.age.toString()
            textEmail.text = user.email
            textPhone.text = user.phone
    
        }
    }

    Kotlin이면 Extension을 사용해서 저런 극악의 코드까지는 나오지 않지만 자바는 저런 형식으로

    전역으로 View를 선언하고 onCreate에서 할당을 해주고 데이터를 넣어준다.

     

    우리가 View라고 생각하는 것은 Xml과 Acitivty 2가지가 해당 된다......!

    하지만 사실상 Xml이 View라고 생각해도 된다.

     

    그래서 GoogleMVVM에서 VMV의 의존성을 떨어트리고 굳이 ActivityView여도 관련 코드는 xml로 다 처리를 해야 겠다! 그리고 Activity에는 오직 비즈니스 로직만 존재를 하여서 View구성은 XML 관련 이벤트 및 비즈니스 코드는 Activity에서 처리를 하자! 고 해서 출시한 것이 DataBinding이다.

     

    AAC에서 제공하는 DataBinding을 이용하여 Activity의 코드를 XML으로 다 옮겨 보도록 해보자

    코드는 아래와 같아진다.


    1. DataBinding 사용 선언하기

    android {
        dataBinding {
            enabled true
        }
    }

    먼저 데이터 바인딩을 사용하려면 build.gradle 모듈단으로 가셔서 위와 같이 dataBinding을 사용한다고 선언해주세요!


    2. View 객체 XML에서 생성하기

    <layout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        <LinearLayout...>
    
    </layout>

    기존 레이아웃을 수정을 해봅시다.

    먼저 기존의 root를 layout으로 한번 감싸줍니다.


    3. 데이터를 바인딩 하기

    <layout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        <data>
    
            <import type="com.example.myapplication.User"/>
    
            <variable
                    name="user"
                    type="User"/>
        </data>
    
    
        <LinearLayout...>
        
    </layout>

    XML에 데이터를 바인딩 하기 위해 data 태그를 이용하여 선언을 해줍니다.

    import태그 를 통해 패키지에 있는 User 객체를 Import 해주고

    variable태그를 통해 변수를 선언 해줍니다.

    <layout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        <data>
    
            <import type="com.example.myapplication.User"/>
    
            <variable
                    name="user"
                    type="User"/>
        </data>
    
    
        <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                android:padding="10dp"
                tools:context=".MainActivity">
    
            <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">
    
                <TextView
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:textSize="20dp"
                        android:text="이름 : "/>
    
                <TextView
                        android:id="@+id/main_view_name"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="3"
                        android:text="@{user.name}"
                        android:textSize="20dp"/>
    
            </LinearLayout>
    
    ...

    @{} 안에 위에 variable으로 선언한 변수.필드값을 넣어줍니다.


    4. 데이터 바인딩이랑 Activity 연결해주기

    class MainActivity : AppCompatActivity() {
    
        lateinit var binding: com.example.myapplication.databinding.ActivityMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
            binding.user = User("홍길동", 20, "010-0000-0000", "gildong02.hong@samsung.com")
        }
    }
    

    기존의 setContentView를 삭제 해주고 DataBindingUtil의 setContetntView로 바꾸어 줍니다.

    여기서 당황 하실텐데 binding 변수의 ActivityMainBinding이 빨간색으로 되어 있을겁니다.

     

    데이터 바인딩은 빌드시 라이브러리가 클래스를 생성해주는 구조이기 때문에 작업을 완료후에

    빌드를 해주면 정상적으로 작동이 됩니다.

     

    그리고 아까 XML에 선언 했던 변수명 user에 데이터를 바인딩 해줍니다.

    그리고 실행 하시면 클래스가 자동으로 생성되고 성공적으로 빌드가 되지는 않을겁니다...

     

    오류는 아래 6번을 보시면 바로 해결 가능하고 빌드가 성공적으로 됩니다.


    6. 데이터 바인딩 XMl 팁!

     

    6-1. text에 데이터는 무조건 String으로 넣어야 한다!

    데이터 바인딩을 통해 text=@{} 안에 데이터를 넣을때 무조껀 String으로 해야 한다...

    그래서 아마 위의 빌드를 오류 시키면 String res를 찾을 수 없다고 뜬다

    그래서 아래와 같이 String으로 변경 해주어야 한다.

     

    Integer.toString(user.age)

     

    참고로 DataBinding은 자바여서 코틀린 문법은 통하지 않는다.

     

    6-2. <import> 규칙

    • xml파일 내에서만 사용해야 하는 특정 클래스를 지정하기 위해 사용 *은 따로 허용하지 않는다.
    • 속성명 변경은 alias 를 사용
    • 제네릭 사용은 <(시작) >(종료) 로 구분한다.
    • static 멤버는 코드에서 객체를 바인딩 하지 않아도 사용 가능

    6-3. <variable> 규칙

    • java.lang 밑에 있는거는 따로 Import가 필요 없다.
    • 데이터의 값이 지정되어 있지 않으면 Default 값으로 지정 된다.
      Int = 0
      Boolean = false
      그 외 = null
    • 별도의 방법으로 default 값 명시가 가능하다
      @{model.name, "가나다"}
    • context 객체는 xml 내장 객체이며 로 선언하지 않아도 xml 전역에서 사용

    6-4. Class Attribute

    • 자동으로 만들어 지는 Binding 클래스명과 패키지 를 변경하고 싶을 때 사용 속성
      <data class="com.example.MyBindingClass">

     

    6-5. Resource Binding

    • @{data.isErrorMsg ? @color/errorBgColor : @color/normalBGColor}
    • 자바이니 3항 연산자도 가능하다!

    6-7. formatted Resource

    • 스트링 리소스에 다음과 같이 저장 "this is string %s %s"
    • @{@string/strFormatResource(data.firstName,data.lstName)}

    6-8. 함수 결과 값 Binding

    • 기존 데이터와 똑같이 사용이 가능하다.

    6-9. Collection

    • 배열처럼 사용이 가능하다.

    6-10. Map Binding

    • 맵은 key 문자열을 묶는 "" 의 충돌 문제가 있어 아래와 처리한다.
      - '@{ " " }'
      - "@{ &quot;  &quot; }"
      - "@{ ` ` }"

     

    6-11. Generic Type 유지

    • 제네릭 사용은 <(시작) >(종료) 로 구분한다. <- 동일 
    • 특정 연산식을 명시도 가능하다.

    6-12. Null 연산자

    • null safety 기능을 바인딩 식에서도 제공한다.
      @{model.displayName ?? model.lastName}
      displayName이 null 이라면 lastName으로
    • NPE 방지
      @{user.name} 에서 user 가 null 이면 전체 결과가 null 로 리턴된다.
      @{user.no} 의 경우 0으로
      String -> null
      int -> 0
      boolean -> false

    7. @BindingAdapter

     

    BindingAdapter는 현재 정의되지 않은 Binding 관련 Attribute를 정의하고 내부 로직을 작성할 때 쓰인다.

    내부 로직을 작성할때 해당 함수는 public static이여야 한다. 코틀린의 경우는 아래와 같이 작성한다.

        companion object {
            @BindingAdapter("bind:bindImage")
            @JvmStatic
            fun bindImage(imageView: ImageView, imageUrl: String) {
                Glide.with(imageView.context)
                    .load(imageUrl)
                    .into(imageView)
            }
        }

    @JvmStatic을 꼭 명시한다.

    <ImageView
    	android:id="@+id/main_image_profile"
    	android:layout_width="100dp"
    	android:layout_height="100dp"
    	bind:bindImage="@{user.profile}"/>

    다른 프로젝트에서 레트로핏을 사용하여서 위와같이 출력을 시도하였다.

    정상적으로 bindImage 함수를 타서 glide로 이미지가 적용된다.


    8. EventBinding

     

    이벤트 바인딩은 2가지 방법이 있다.


    1. 매개 변수를 맞추었을 경우 함수 방식
    2. 매개 변수를 맞추지 않을 경우는 리스너 방식

     

    함수 방식 :  "::" 을 이용하여 이벤트시 호출되어야 하는 함수 지정한다.
    왜냐하면 컴파일 단계에서 바인딩이 참조 되기 때문이다.

    아래와 같이 함수를 선언하고 XML을 구성하면


    public void onClickHandler() {
        Log.d("key","value")
    }

    아래 구문은 함수 타입이 맞지 않아서 에러가 난다.
    <android:onClick="@{handler.onClickHandler()}"/>

    컴파일시 판단하여 실행하기 때문에 에러가 나지 않는다.
    <android:onClick="@{handler::onClickHandler}"/>

    함수명은 임의로 지정이 가능하지만 매개 변수를 맞추어 주어야 한다.

    리스너 방식 : 이벤트 핸들러로 개발자 람다 함수를 등록하는 방식이다.
    런타임 시점에서 바인딩이 참조된다.

    android:onClick = "@{() -> handler.onClickListener1()}"
    android:onClick = "@{() -> handler.onClickListener2(model)}"
    android:onClick = "@{(view) -> handler.onClickListener3(view,model)}"


    9. Model 변경 감지


    Observable 객체

        1. 바인딩 시켜야 하는 클래스를 만들때 BaseObservable 를 상속한다.
        2. 변경가능한 변수에 @Bindable 어노테이션 작성한다.
        3. notifyPropertyChanged 함수를 사용하여 변경시 데이터 변경 요청한다.

    notifyPropertyChange(BR.변수) 를 사용

    해당 Bindable의 값이 변동되면 해당 클래스의 모든 변수가 적용 된다.
    다만, @Bindable 어노테이션이 적용된 변수의 한에서만 적용된다.

    @Bindable 
    String name; 
    
    int age; 

    위의 두 변수에서 name의 값이 변경되면 name과 age의 값이 반영 되고
    age의 값이 변경되면 별다른 작동은 일어나지 않는다.

    Observable 필드

    ObservableField<String>
    타입을 Observable자료형 또는 ObservableParcelable 으로 선언을 한다.
    별도의 notify 함수를 사용하지 않아도 자동으로 적용 된다.

    Observable 컬렉션

    ObservableArrayMap<String,Object>
    변경 감지가 필요한 데이터만 따로 모아 Collection 타입으로 관리한다

    Observable 필드와 동일하다.

    댓글

Designed by Tistory.