티스토리 뷰

반응형
SMALL

안드로이드를 처음 배울 때 우리는 레이아웃을 XML을 사용하여 구현하였다.

그리고 activity class에서 view를 binding하여 기능을 구현하곤 하였다. 아래처럼 말이다.

public class MainActivity extends AppCompatActivity {
	private Button button;
    
	@Override
    protected void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = findViewById(R.id.button);
    }
}

보통 위와 같이 findViewById 메소드를 사용하여 구현하였을 것이다. 그런데 생각해보자. 만약에 위에 처럼 버튼이 1개, 2개 등 적은 수의 뷰가 있다면 크게 상관 없겠지만 100개 200개의 버튼을 위의 방식으로 구현한다고 생각해보자. FindViewByid... FindViewById....

생각만해도 끔찍할 것이다.

 

How) 그럼 이런 짓을 안할 수는 없을까?

이를 위해 존재하는 아주 좋은 친구가 있다. 바로 Data Binding이라는 친구다. 굳이 findViewById처럼 뷰의 id값으로 불러오는 게 아니라 레이아웃과 클래스를 Binding하여 바로바로 사용할 수 있도록 해주는 것이다. 

더 나아가 RxJava를 사용하여 Observable을 붙여주게 된다면, 필드 값이 변할 때마다 실시간으로 뷰에 자동으로 적용할 수 있게 된다.

 

 

시작 및 레이아웃

가장 먼저 해야할 일은 gradle 파일에서 data binding을 사용하도록 해주는 설정을 하는 것이다.

아래 구문을 gradle파일에 추가해보자.

android {
	....
	dataBinding{
		enabled = true
	}
}

 

우선 레이아웃 파일을 살펴보도록 하자.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="activity"
            type="{packageName}.MainActivity" />
    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/bt_start"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{activity.btnString}"
            tools:layout_editor_absoluteX="0dp"
            tools:layout_editor_absoluteY="0dp" />
    </android.support.constraint.ConstraintLayout>
</layout>

기본적인 구조는 위와 같다. 전체 레이아웃을 <layout>이라는 태그로 감싸게 된다. 이 레이아웃에 Data 태그와 Variable 태그가 들어가게 된다.

variable에서 type부분에 {packageName}.MainActivity 는 com.example.TestApp.MainActivity처럼 사용하는 부분이다.

패키지 주소를 넣어주면 된다.

 

레이아웃은 이렇게 구성해주고 아래에 Button에서 android text 부분을 보자 @{activity.btnString}이라는 부분이 있는데 이 부분은 Activity에서 특정 Field Variable의 값을 지정해주면 그 값이 Button에 Text로 나타나는 것을 의미한다.

레이아웃을 다 구현했다면 Activity를 구현할 차례다. 아래 코드를 살펴보자.

 

Activity 구현

public class MainActivity extends AppCompatActivity {
	ActivityMainBinding binding;
	public String btnString="Test String";
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
		binding.setActivity(this); 
		// setContentView(R.layout.activity_main);
	}
}

여기서 알아두어야 할 점은 ActivityMainBinding이라는 TYPE이다. 이 TYPE은 사용자 정의 타입이 절대 아니다!!

layout xml파일명에 따라서 자동으로 생성되는 타입이 되는 것이다.

현재 위 코드를 보면 activity_main이 xml파일명이므로 ActivityMainBinding으로 CamelCase 형태로 변환되어 타입이 생성된 것을 확인해야 한다!

 

위 Layout에서 보았던 btnString이라는 것은 Activity의 Field Variable을 뜻한다. String btnString이 보인다면 당신은 성공한 것이다.

 

그리고 SetContentView를 사용하지 않는다. 대신 DataBindingUtil.setContentView라는 메소드를 통해 Binding시켜주게 된다. 기억하도록 하자.

 

추가로 binding.setActivity(this);로 Activity와 레이아웃을 연결하는 작업을 반드시 해야한다.

만약에 안한다면 어떻게 될까?

 

Binding Error가 발생할 것이다. (찾을 수 없다는 등...)

위 코드를 한번 적용해보고 실행해보도록 하자.

 

 

Binding Adapter 사용해보기

이번에는 ImageView에 src를 어떻게 연동할 것인지 예제를 통해 알아가보는 시간을 가져보도록 하자.

<?xml version="1.0" encoding="utf-8"?>
<layout 
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <data>
        <variable
            name="data"
            type="com.example.Data"/>
    </data>
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@{data.resid}"/>
            
    </RelativeLayout>
</layout>

자 여러분들은 위와 같이 레이아웃을 구성하면 이미지가 적용이 될 것이라고 생각할 것이다.

하지만 실제로 실행해보면 전혀 작동하지 않는 다는 것을 알게 될 것이다.

imageview의 src에 이미지를 넣기 위해서는 BindAdapter Annotation을 이용하여 함수 바인딩이 필요하다.

import android.databinding.BindingAdapter;
import android.widget.ImageView;

public class TestBindingAdapter {
    
    @BindingAdapter({"imgRes"})
    public static void imgload(ImageView imageView, int resid) {
        imageView.setImageResource(resid);
    }
}

이렇게 사용할 수 있다. 중요한 부분이 imgload static 메소드이다. public static void 로 정의 한 다음에 적용 하고 싶은 View를 Parameter 첫번째에 넣는다. 그리고 두번째 Parameter 부터는 받아오는 값을 넣는다.

그리고 BindingAdapter annotation으로 메소드를 정의한다.

"bind:imgRes" 가 xml 에서 "app:imgRes"로 사용할 예정이다. 여기서  app은 아래와 같은 xml 네임 스페이스이다.

xmlns:app="http://schemas.android.com/apk/res-auto"

imgResimgload(Method Name)은 꼭 같은 필요가 없다. 

<?xml version="1.0" encoding="utf-8"?>
<layout 
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <data>
        <variable
            name="data"
            type="com.example.Data"/>
    </data>
    
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@{data.resid}"/>
            
    </RelativeLayout>
</layout>

annotation으로 정의된 이름으로 정의 한 다음에 Binding을 시키면 된다.

이렇게 클래스에 static Method를 BindingAdapter라는 annotation만 정의하면 xml에서 편하게 이미지를 부를 수도 있다.

 

2-Way Binding 사용해보기

이번에는 2-Way Binding을 사용해보자. EditText에 TextWatcher를 붙이지 않고 데이터가 반영될 수 있도록 해보자.

우선 레이아웃 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.yappbob.databindingpractice.TextViewModel" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/textViewResult"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/editTextInput"
            android:text="@={viewModel.contents}" />

        <EditText
            android:id="@+id/editTextInput"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="@={viewModel.contents}" />

    </RelativeLayout>
</layout>

레이아웃에서는 크게 다른 것은 없다. 하지만 중간에 TextView와 EditText의 text 속성을 잘 보자.

 

@={viewModel.contents}

ObservableField 타입 변수를 만들어 줄 것인데 이 변수가 EditText의 입력 데이터를 Observe 하고 있다가 데이터의 변화를 감지하면 바로 TextView에 적용하는 것을 뜻한다.

 

그럼 이 viewModel을 구현해보자. 코드는 간단하다. 딱 1줄이면 된다.

import androidx.databinding.ObservableField;

public class TextViewModel {
    public ObservableField<String> contents = new ObservableField<>();
}

TextViewModel을 Instance화해서 binding.setViewModel()의 Parameter로 넣으면 Binding이 된다.

위의 레이아웃에서 <variable>에 actiivty가 아닌 viewmodel 즉, TextViewModel을 바인딩했기 때문에 contents라는 필드변수를 사용할 수 있다는 것이다. 이제 MainActivity 코드를 살펴보도록 하자.

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;

import android.os.Bundle;

import com.yappbob.databindingpractice.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.setViewModel(new TextViewModel());
    }
}

첫 번째 코드와는 다른 점이 하나 눈에 띈다. 바로 binding.setViewModel()이라는 부분이다.

이는 자동으로 생성되는 메소드이며 만약 레이아웃에서 viewModel을 activity라고 했다면 setActivity() 메소드가 자동으로 생성되었을 것이다. 

 

자 코드는 이로서 모두 작성을 완료하였다. 앱을 빌드하고 실행해보도록 하자. EditText에 텍스트를 입력하면 바로 TextView에 적용 되는 것을 볼 수 있다.

반응형
LIST
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/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
글 보관함