춤추는 개발자

[AOS] Room + ViewModel + LiveData + RecyclerView (추가) 본문

Android/study_til

[AOS] Room + ViewModel + LiveData + RecyclerView (추가)

Heon_9u 2021. 6. 29. 16:10
728x90
반응형

 이번 포스팅에서는 지금까지 했던 예제에 DataBinding을 적용해보겠습니다.

 

 DataBinding이란, 간단하게 설명하자면 Layout의 UI 구성요소를 프로그래밍 방식(findViewById 등)이 아닌 선언적인 형태를 사용하는 것입니다. 즉, UI의 구성 요소 중 View에 데이터 소스를 Binding하는 작업을 DataBinding이라고 합니다.

 이론 설명보다 직접 코드를 보며 어떤 차이가 있는지 확인하며 이해하도록 하겠습니다!

 

 

가장 먼저, build.gradle에 dependency를 추가합니다.

android {
    buildFeatures {
         dataBinding true
    }
}

 

DataBinding의 XML 정의

 기존 코드와는 달리 <layout>이라는 태그로 감싸며 각 View에 Binding시킬 Data를 <data>를 통해 선언합니다.

만약, import 작업이 필요한 경우, <import>를 사용할 수 있습니다.

<layout>
  <!-- data 정의 -->
  <data>
    <variable name="obj" type="Item"/>
  </data>
  
  <!-- layout 정의 -->
  <LinearLayout>
  
     ...
     
  </LinearLayout>
</layout>

 

1. note_item

 RecyclerView의 item layout인 note_item에 위와 같은 형태의 코드를 작성해줍니다. 각 TextView의 android:text 요소를 보면 "@{ ... }"를 확인할 수 있습니다. 이는 각 View에 적절한 데이터 소스를 설정해주는 작업입니다. 다만, 만약 객체의 컬럼 중 문자열이 아닌 경우, 반드시 String 형태로 미리 바꿔줘야 합니다.

 

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="note"
            type="com.heon9u.aacproject.Note"/>
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp">

            <TextView
                android:id="@+id/text_view_priority"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:text="@{String.valueOf(note.priority)}"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"/>

            <TextView
                android:id="@+id/text_view_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@{note.title}"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:maxLines="1"
                android:layout_toStartOf="@id/text_view_priority"
                android:layout_alignParentStart="true"
                android:ellipsize="end"/>

            <TextView
                android:id="@+id/text_view_description"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/text_view_title"
                android:text="@{note.description}"/>

        </RelativeLayout>

    </androidx.cardview.widget.CardView>
</layout>

 

 

2. NoteAdapter

 이번에는 RecyclerView에 List 객체를 Binding해주는 Adapter코드를 수정합니다. (편의상 import문은 삭제했습니다.)

바뀐 메서드는 총 3개입니다. 먼저, NoteHolder Class에서 기존 TextView와 ItemView대신 NoteItemBinding을 활용합니다.

 

 Binding Class의 이름은 이미 정해져있습니다. 만약, simple_layout.xml에서 DataBinding을 사용할 경우, Java 코드에서 SimpleLayoutBinding으로 생성됩니다.

 

 두번째로 Holder가 변경됨에 따라 onCreateViewHolderonBindViewHolder의 코드를 수정합니다. 

기존에는 onBindViewHolder에서 Holder에 데이터를 하나씩 set했습니다. 반면에, DataBinding은 한 줄로 코드를 요약할 수 있습니다.

 

import com.heon9u.aacproject.databinding.NoteItemBinding;

public class NoteAdapter extends ListAdapter<Note, NoteAdapter.NoteHolder> {
    private OnItemClickListener listener;

    public NoteAdapter() {
        super(DIFF_CALLBACK);
    }

    private static final DiffUtil.ItemCallback<Note> DIFF_CALLBACK = new DiffUtil.ItemCallback<Note>() {
        @Override
        public boolean areItemsTheSame(@NonNull Note oldItem, @NonNull Note newItem) {
            return oldItem.getId() == newItem.getId();
        }

        @Override
        public boolean areContentsTheSame(@NonNull Note oldItem, @NonNull Note newItem) {
            return oldItem.getTitle().equals(newItem.getTitle()) &&
                    oldItem.getDescription().equals(newItem.getDescription()) &&
                    oldItem.getPriority() == newItem.getPriority();
        }
    };

    @NonNull
    @Override
    public NoteHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.note_item, parent, false);
        NoteItemBinding itemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                                            R.layout.note_item, parent, false);
        return new NoteHolder(itemBinding);
    }

    @Override
    public void onBindViewHolder(@NonNull NoteHolder holder, int position) {
        Note currentNote = getItem(position);
        holder.itemBinding.setNote(currentNote);
    }

    public Note getNoteAt(int position) {
        return getItem(position);
    }

    class NoteHolder extends RecyclerView.ViewHolder {
        private final NoteItemBinding itemBinding;

        public NoteHolder(@NonNull NoteItemBinding itemBinding) {
            super(itemBinding.getRoot());
            this.itemBinding = itemBinding;

            itemView.setOnClickListener(v -> {
                int position = getAdapterPosition();
                if (listener != null && position != RecyclerView.NO_POSITION) {
                    listener.onItemClick(getItem(position));
                }
            });
        }
    }

    public interface OnItemClickListener {
        void onItemClick(Note note);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

 

 

 여기까지 DataBinding을 이용한 작업을 마쳤습니다. Java코드를 요약한다는 장점은 있지만, XML코드가 늘어난다는 단점이 있습니다. 보통 XML보다 Java 코드의 양이 많기 때문에 스케일이 큰 어플리케이션의 경우, DataBinding의 효율이 높아진다고 생각합니다.

 

 특히, DataBinding과 LiveData 함께 활용한다면, 장점은 배가 됩니다.

  • findViewById()를 쓰지않아도 View에 적절한 데이터를 설정할 수 있다.
  • Data가 실시간으로 변경될 때, Observer를 활용하면 View도 함께 변경된다.
  • ListView나 RecyclerView를 활용할 때, 각 item을 세팅해주는 작업을 DataBinding으로 짧게 구현할 수 있다.

 

 

728x90
반응형