프로젝트 [코테 복습] - ViewBinding & DataBinding 적용(1)

유의선·2024년 6월 15일

코테 복습 프로젝트

목록 보기
13/13

진행중인 프로젝트에 ViewBinding과 DataBinding을 적용하도록 수정하여 MVVM 패턴을 적용해보려고 해보았다.


먼저 build.gradledataBindingviewBinding을 추가하였다.

android {
    
    ...

    buildFeatures{
        viewBinding = true
        dataBinding = true
    }
}

MainActivity에 ViewBinding 적용

MainActivity.java에 ViewBinding을 적용하였다.

public class MainActivity extends AppCompatActivity implements onItemSwipeListener {

    ...
    
    private ActivityMainBinding mainBinding;
    
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // View Binding
        mainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mainBinding.getRoot());

        ...

        // floatingButton goto AddActivity
        mainBinding.floatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), AddActivity.class);
                makeResultLauncher.launch(intent);
            }
        });

        ...

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        mainBinding.recyclerView.setLayoutManager(layoutManager);
        mainBinding.recyclerView.setAdapter(adapter);

        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new TaskItemTouchHelperCallback(this));
        itemTouchHelper.attachToRecyclerView(mainBinding.recyclerView);

        loadTaskListData();
    }

MainActivity에서 사용하는 View는 RecyclerView와 FloatingButton 둘 밖에 없으므로 DataBinding을 사용할 필요를 느끼지 못했다. 그래서 ViewBinding만 적용시켰다.


AddActivity에 DataBinding 적용

AddActivity에 DataBinding을 적용하였다.

우선 AddActivity에서 MainActivity로 액티비티를 전환하는 이벤트에 사용할 Event Wrapper를 만들었다.

package com.example.codingtest_reviewe;

public class Event<T> {
    private T content;
    private boolean hasBeenHandled = false;

    public Event(T content){
        this.content = content;
    }

    public T getContentIfNotHandled() {
        if(hasBeenHandled)
            return null;
        else{
            hasBeenHandled = true;
            return content;
        }
    }

    public T peekContent() {
        return content;
    }
}

다음으로 DataBinding에 사용할 AddViewModel을 만들었다.
LiveData로 제목/년도/날짜/주소 를 저장하는 객체들을 만들었다.
메소드로는 EditTextonTextChanged에 사용할 제목/주소를 저장하는 메소드와
DatePicker를 띄우는 메소드,
입력값을 확인하고 MainActivity로 액티비티를 전환하는 메소드,
Toast 메세지를 띄우도록 하는 메소드를 만들었다.

public class AddViewModel extends ViewModel {

    private MutableLiveData<String> title = new MutableLiveData<>();
    public LiveData<String> getTitle() {
        return title;
    }
    private MutableLiveData<String> yearString = new MutableLiveData<>();
    public LiveData<String> getYearString() {
        return yearString;
    }
    private MutableLiveData<String> dateString = new MutableLiveData<>();
    public LiveData<String> getDateString() {
        return dateString;
    }

    private MutableLiveData<String> address = new MutableLiveData<>();
    public LiveData<String> getAddress() {
        return address;
    }

    private MutableLiveData<String> toastMessage = new MutableLiveData<>();
    public LiveData<String> getToastMessage() {
        return toastMessage;
    }

    private MutableLiveData<Event<String>> addTaskEvent = new MutableLiveData<>();
    public LiveData<Event<String>> getAddTaskEvent() {
        return addTaskEvent;
    }

    public void setTitle(CharSequence s, int start, int before, int count) {
        title.setValue(s.toString());
    }

    public void setAddress(CharSequence s, int start, int before, int count){
        address.setValue(s.toString());
    }

    public void showDatePicker(Context context){
        DatePickerDialog.OnDateSetListener dateSetListener = new DatePickerDialog.OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker datePicker, int year, int month, int day) {
                month = month + 1;

                yearString.setValue(Integer.toString(year));
                dateString.setValue(month + "/" + day);
            }
        };

        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int style = AlertDialog.THEME_HOLO_LIGHT;

        DatePickerDialog datePickerDialog = new DatePickerDialog(context, style, dateSetListener, year, month, day);
        datePickerDialog.show();
    }

    public void onButtonClicked(){
        if(title.getValue() == null || title.getValue().isEmpty()) {
            setToast("제목을 입력해주세요");
        } else if(yearString.getValue() == null || yearString.getValue().isEmpty() || dateString.getValue() == null || dateString.getValue().isEmpty()){
            setToast("날짜를 입력해주세요");
        } else if(address.getValue() == null || address.getValue().isEmpty()){
            setToast("주소를 입력해주세요");
        } else {
            addTaskEvent.setValue(new Event<String>("add event"));
        }
    }

    public void setToast(String s) {
        toastMessage.setValue(s);
    }
}

activity_add.xml 레이아웃 파일에 DataBinding을 적용하였다.

<?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.example.codingtest_reviewe.AddViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="40dp">

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ADD TASK"
            android:textSize="34sp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <EditText
            android:id="@+id/editTextTask_add"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:background="@drawable/input_drawable"
            android:ems="10"
            android:hint="Task"
            android:inputType="text"
            android:paddingLeft="10dp"
            android:paddingTop="20dp"
            android:paddingRight="10dp"
            android:paddingBottom="20dp"
            android:text="@{viewModel.title}"
            android:onTextChanged="@{viewModel.setTitle}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2" />

        <Button
            android:id="@+id/buttonAdd"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:text="ADD"
            android:textSize="24sp"
            android:onClick="@{() -> viewModel.onButtonClicked()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

        <FrameLayout
            android:id="@+id/frameLayout"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/editTextTask_add">

            <Button
                android:id="@+id/buttonSetTime_add"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:backgroundTint="#00000000"
                android:onClick="@{() -> viewModel.showDatePicker(context)}"
                android:ems="10"
                android:inputType="text" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/input_drawable"
                android:orientation="horizontal"
                android:paddingLeft="10dp"
                android:paddingTop="20dp"
                android:paddingRight="10dp"
                android:paddingBottom="20dp">

                <TextView
                    android:id="@+id/textYear_add"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center_vertical"
                    android:hint="Date"
                    android:textSize="18sp"
                    android:text="@{viewModel.yearString}"/>

                <TextView
                    android:id="@+id/textDate_add"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center_vertical"
                    android:paddingLeft="20dp"
                    android:textSize="18sp"
                    android:text="@{viewModel.dateString}"/>

            </LinearLayout>
        </FrameLayout>

        <EditText
            android:id="@+id/editTextAdd_add"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="30dp"
            android:background="@drawable/input_drawable"
            android:ems="10"
            android:hint="Address"
            android:inputType="text"
            android:paddingLeft="10dp"
            android:paddingTop="20dp"
            android:paddingRight="10dp"
            android:paddingBottom="20dp"
            android:text="@{viewModel.address}"
            android:onTextChanged="@{viewModel.setAddress}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/frameLayout" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

마지막으로 AddActivity.java 파일의 코드를 수정하였다.

public class AddActivity extends AppCompatActivity {

    private ActivityAddBinding addBinding;
    private AddViewModel addViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Data binding
        addBinding = ActivityAddBinding.inflate(getLayoutInflater());
        setContentView(addBinding.getRoot());

        addViewModel = new ViewModelProvider(this).get(AddViewModel.class);

        addBinding.setViewModel(addViewModel);
        addBinding.setLifecycleOwner(this);

        addViewModel.getToastMessage().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Toast.makeText(getApplicationContext(),s , Toast.LENGTH_SHORT).show();
            }
        });

        addViewModel.getAddTaskEvent().observe(this, new Observer<Event<String>>() {
            @Override
            public void onChanged(Event<String> event) {
                if(event != null && event.getContentIfNotHandled() != null){

                    Intent intent = new Intent();
                    intent.putExtra("task", addViewModel.getTitle().getValue());
                    intent.putExtra("date", addViewModel.getYearString().getValue() + " " + addViewModel.getDateString().getValue());
                    intent.putExtra("address", addViewModel.getAddress().getValue());
                    setResult(RESULT_OK, intent);
                    finish();
                }
            }
        });
    }
}

getToastMessage()를 observe 하여 Toast 메세지를 띄우고,
getAddTaskEvent()를 observe 하여 Intent를 통해 액티비티를 전환하도록 만들었다.

0개의 댓글