Databinding을 사용하여 RecyclerView를 달아보자
728x90
반응형

이렇게 data를 줄줄이 달아보자. 이번엔 Databinding을 사용해서.



RecyclerView는 data가 늘어나고 줄어들 때

리스트뷰 형태로 보여주기 유연한 View이지만

data 포맷이 바뀌면 매번마다 어댑터를 손봐야하는 불편함이 있다.


이를 개선하기 위해 Databinding을 사용하여

RecyclerView를 이용해보기로 하였다.



1. 줄줄이 엮어줄 Item부터 - MyItem.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyItem {
 
    boolean selectItem;
    String name;
    String mydate;
    
    public ApprovalItem() {
    }
 
    public ApprovalItem(boolean selectItem, String nameString mydate) {
        this.selectItem = selectItem;
        this.name = name;
        this.mydate = mydate;
    }
 
    // 생략
    // Getter, Setter
}
cs


item_my.xml은 생략하겠다.

위 요소가 들어갈 TextView를 자유롭게 만들면 된다.



2. 주인공 RecyclerView 등장 - MyFragment.java

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
32
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">
 
    <data>
        <import type="android.databinding.ObservableArrayList" />
        <import type="io.truecoupon.app.content.my.MyItem" />
 
        <variable
            name="myList"
            type="ObservableArrayList&lt;MyItem&gt;" />
 
    </data>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".content.my.MyFragment">
 
        <!-- 생략 -->
 
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview_my_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager"
            app:item="@{myList}"/>
    </LinearLayout>
</layout>
cs


ObservableArrayList를 사용한 이유.

UI가 변경될 때, data도 바로 변경되고 반대로도 가능하게 하기 위해서.


11번째 줄의 <variable>은 30번째 줄에 사용된다.

adapter에서 편하게 item을 list에 넣을 수 있다.



3. MyFragment.java


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MyFragment extends BaseFragment<MyPresenter.MyUi>
        implements MyPresenter.MyUi {
 
    public static String TAG = "MyFragment";
 
    private ObservableArrayList<MyItem> myItems;
    private MyAdapter mAdapter;
    private MyPresenter mPresenter;
 
    // 생략
 
    // 자체적으로 만든 Code
    // 무시해도 좋다.
    @Override
    protected View createRootView(@NonNull LayoutInflater inflater, 
            @Nullable ViewGroup container) {
              
        return inflater.inflate(R.layout.fragment_my, container, false);
    }
 
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, 
            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
          
        super.onCreateView(inflater, container, savedInstanceState);
        FragmentMyBinding binding = DataBindingUtil
          .inflate(inflater, R.layout.fragment_my, container, false);
 
        // Sample date : "M", "20180801"... todo : remove soon
        mPresenter = new MyPresenter();
        // 생략
        mPresenter.mockGetMyList("M""20180801");
 
        mAdapter = new MyAdapter();
        myItems = new ObservableArrayList<>();
 
        binding.recyclerviewMyList.setAdapter(mAdapter);
        binding.setMyList(myItems);
 
        return binding.getRoot();
    }
    
    // 생략
}
 
cs


앞에서(2번 항목) 레이아웃을 정상적으로 만들었다면

FragmentApprovalBinding을 만들 수 있다.

이제 이 binding으로 간단하게 선언할 뷰들은

편하게 호출할 수 있게 되었다.


여기서는 binding으로 recyclerView를 가지고 와서 setAdapter를 하고

레이아웃의 <variable>로 맞춰진 myList를 set하고 있다.

이것으로 Fragment에서 item을 끼울 준비는 다 했다. (쏘 심플~)

이제 Adapter를 만들자.



4. MyAdapter


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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
    public static String TAG = "MyAdapter";
 
    private List<MyItem> myItems;
 
    public MyAdapter() {
        this.myItems = new ArrayList<>();
    }
 
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemMyBinding binding = ItemMyBinding.inflate(
            LayoutInflater.from(parent.getContext()), parent, false);
        return new MyViewHolder(binding);
    }
 
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        MyItem item = myItems.get(position);
        holder.bind(item);
    }
 
    @Override
    public int getItemCount() {
        return myItems.size();
    }
 
    void setItem(List<MyItem> items) {
        if (items != null) {
            this.myItems = items;
            notifyDataSetChanged();
        }
    }
 
    @BindingAdapter("bind:item")
    public static void bindItem(RecyclerView recyclerView, ObservableArrayList<MyItem> items) {
        MyAdapter adapter = (MyAdapter) recyclerView.getAdapter();
 
        // 생략
 
        if (adapter != null) {
            adapter.setItem(items);
        }
    }
 
    class MyViewHolder extends RecyclerView.ViewHolder {
 
        ItemMyBinding binding;
 
        public MyViewHolder(ItemMyBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
 
        // item_my.xml : <variable> 정보를 취함
        void bind(MyItem item) {
 
            String price = String.format("%,d", 20000);
 
            // 생략
 
            binding.setVariable(BR.my, item);
            binding.setVariable(BR.price, price);
        }
    }
}
cs



먼저 1번 항목에서 안내한 item_my.xml에 layout 태그를 포함한 data binding 준비가 되어 있어야 한다.

그래야 viewholder를 만들기 위한 ItemMyBinding을 생성할 수 있다.

그냥 넘어가려고 했지만 언급해야 덜 찝찝할거 같아서 아래에 공유한다.



item_my.xml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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="my"
            type="io.truecoupon.app.content.my.MyItem"/>
        <variable
            name="price"
            type="String"/>
    </data>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
 
        <!-- 내용은 item_my.java를 참고하여 알아서 채우시길 -->
 
    </LinearLayout>
 
</layout>
 
cs



자... item 준비도 완료되었다. 다시 adapter로 돌아가보자.

onBindViewHolder에서 item을 bind하기 위해 MyViewHolder 아래에 bind 메소드를 추가했다.

BR 클래스로 <variable> 변수명을 찾아서 bind되는 과정이다.


Databinding으로 인해 쉬원진 것 중 하나가 바로 @BindingAdapter 이다.

"bind:item"이라고 되어 있는 부분은 fragment.xml에서 본 app:item="@{myList}"이다.

adapter에서 recyclerView 위에 item을 바로 set할 수 있게

databinding에서 이 어노테이션을 제공하고 있다.



Presenter는 서버와 통신하는 부분이라 이번에 다룰 주제와 밀접하지가 않다.

그래서 Presenter 부분은 생략한다.


* 주의사항1


RecyclerView에는 data를 쌓기 위한 레이아웃 형태를 아래처럼 만들어 줘야 한다.


1
2
3
binding.recyclerviewMyList.setLayoutManager(
  new LinearLayoutManager(getActivity(), LinearLayout.VERTICAL, false)
);
cs


하지만 더 간단하게 할 수 있는 방법이 있다.

아래처럼 xml에서 바로 layoutManager를 처리할 수 있다.

위 샘플코드에는 아래 형태로 되어 있다.


1
2
3
4
5
6
<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_approval_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="android.support.v7.widget.LinearLayoutManager"
    app:item="@{approvalList}"/>
cs





* 주의사항2


만약 아래와 같은 에러를 발견한다면

@BindingAdapter 메소드에 Static으로 적용되어 있지 않았기 때문이다.


1
2
3
4
5
6
java.lang.IllegalStateException: 
Required DataBindingComponent is null in class FragmentApprovalBinding. 
A BindingAdapter in (pkg명 생략).MyAdapter is not static and requires an object to use, 
retrieved from the DataBindingComponent. 
If you don't use an inflation method taking a DataBindingComponent, 
use DataBindingUtil.setDefaultComponent or make all BindingAdapter methods static.
cs



참고 자료



728x90
반응형