728x90
반응형

이 글은 MVP 패턴과 DataBinding 라이브러리를 모르면 이해하기 힘든 글입니다.


Android에서 DataBinding은 ButterKnife, KotlinExtension과 같이 편리하고 유용하게 사용할 수 있는 Library 입니다.


저는 MVP+DataBinding 구조로 개인프로젝트를 진행하고 있습니다.

(실제로 이 방식은 deprecated 된 방식입니다.)


이렇게 MVP와 DataBinding을 사용하게 되면 View에 Click Listener를 등록한 다음 Presenter의 함수를 호출하지 않고 바로 xml에서 Presenter의 함수를 호출하는 효과를 볼 수 있습니다.

(내부적으로는 앞에 이야기 한 방식으로 하고 있습니다.)


하지만 layout에 EditText가 있고 값을 가져와서 저장해야 할 때 저는 Presenter가 아닌 View의 함수를 호출해서 사용했습니다.(ㅠㅠ)


그래서 억지로 억지로 사용하다가 오늘 Two Way Binding에 대해 알게 되었습니다.


그럼 이제 예를 통해 오늘의 주제인 DataBinding Two Way Binding에 대해 알아보겠습니다.


  


이번 글에서 예제로 사용할 화면입니다.


왼쪽 화면처럼 이름, 이메일, 폰번호를 입력하고 저장 버튼을 누르면 오른쪽 화면처럼 요약을 보여주려고 합니다.


먼저 Two Way Binding을 모른다고 가정하고 진행해보겠습니다.



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

<data>

<variable
name="view"
type="com.googry.googrydatabindingtwowaybinding.ui.unknown.UnknownContract.View"/>

<variable
name="user"
type="com.googry.googrydatabindingtwowaybinding.data.User"/>

</data>

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

<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name"
android:text="@{user.name}"/>

<EditText
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/email"
android:text="@{user.email}"/>

<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/phone"
android:text="@{user.phone}"/>

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> view.onSaveClick()}"
android:text="@string/save"/>

</LinearLayout>
</layout>


layout 입니다.


User를 받아와서 각 EditText에 값을 넣어줍니다.

UnknownContract.View를 받아와서 onClick 이벤트를 받아줍니다.


public interface UnknownContract {
interface Presenter extends BasePresenter {
void save(String name, String email, String phone);
}

interface View extends BaseView<Presenter> {
void setUser(User user);

void showSaveResult(User user);

void onSaveClick();
}

}

UnknownContract 입니다.


Presenter는 name, email, phone을 입력받는 save()를 선언합니다.


View는 setUser(), showSaveResult()와 버튼 클릭 이벤트를 받는 onSaveClick()을 선언합니다.


@Override
public void onSaveClick() {
mPresenter.save(
mBinding.etName.getText().toString(),
mBinding.etEmail.getText().toString(),
mBinding.etPhone.getText().toString()
);
}

UnknownFragment안에 onSaveClick()를 구현했습니다.

이 부분에서 EditText의 내용을 가져와 Presenter에 save()함수를 통해 전달해 줍니다.


@Override
public void save(String name, String email, String phone) {
mUser.name = name;
mUser.email = email;
mUser.phone = phone;

mUserDataSource.saveUser(mUser);
mView.showSaveResult(mUser);
}

UnknownPresenter안에 save()를 구현했습니다.


내부적으로 mUser에 값을 넣어주고 UserDataSource에 User를 저장하고 View에 결과를 보여주라고 showSaveResult()함수를 호출 합니다.


@Override
public void showSaveResult(User user) {
new AlertDialog.Builder(getContext())
.setMessage(user.toString())
.show();
}

그리고 UnknownFragment에서 AlertDialog를 통해 결과를 보여주게 됩니다.


이렇게 순서가 xml -> View.onSaveClick() -> Presenter.save() -> View.showSaveResult() 입니다.


하지만 버튼 클릭을 했을 때 각 EditText내용의 값을 가져오면서 View의 함수를 호출하는 것이 아닌 바로 Presenter의 함수를 호출 할 수 있을까요?

다르게 말하자면 View.onSaveClick()을 호출하지 않고 xml -> Presetner.save() -> View.showSaveResult()를 할 수 있을까요?


네, 바로 Two Way Binding을 하게 되면 할 수 있습니다.


그럼 이제 Two Way Binding을 하는 방법에 대해 알아보겠습니다.


private User mUser;

@Override
public void start() {
mView.setUser(mUser);
}

Presenter안에 User가 있고 View에 User객체를 전달해줍니다.


그리고 Contract.View에 있는 setUser()라는 함수의 구현부분은 이렇게 생겼습니다.

@Override
public void setUser(User user) {
mBinding.setUser(user);
}

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

<data>

<variable
name="presenter"
type="com.googry.googrydatabindingtwowaybinding.ui.known.KnownContract.Presenter"/>

<variable
name="user"
type="com.googry.googrydatabindingtwowaybinding.data.User"/>

</data>

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

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name"
android:text="@={user.name}"/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/email"
android:text="@={user.email}"/>

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/phone"
android:text="@={user.phone}"/>

<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> presenter.save()}"
android:text="@string/save"/>

</LinearLayout>
</layout>

저렇게 binding에 넣어준 User를 @{}가 아닌 @={}로 각 EditText에 binding 하면 됩니다.


이렇게 했을 경우 EditText에서 text가 변경 될 때 마다 User안에 값을 변경하게 됩니다.


User객체는 Presenter에서 관리하고 있기 때문에 그럼 각 EditText에서 값을 가져와 Presenter에 전달해줄 필요가 없게 됩니다.


그래서 바로 버튼에 onClick에서 presenter.save()를 호출 할 수 있게 되는 것입니다.




이렇게 Two Way Binding을 사용하게 되면 바로 Presenter의 함수를 호출하여 함수 호출의 depth가 낮아지게 되는 효과를 볼 수 있습니다.


예제 코드는 https://github.com/sjjeong/GoogryDataBindingTwoWay 에서 확인하실 수 있습니다.

728x90
반응형

+ Recent posts