[android] MVVM – 5. RecyclerView

================ 2017.06.07 수정 ================

https://stackoverflow.com/documentation/android/169/recyclerview/18296/recyclerview-with-databinding#t=201706071246198231184 의 ViewHolder 를 적용했다.

 


얼른 넘어가야하는데, 손이 안 가서 실력이 안 늘고 있는 RecyclerView도 써보자. 아무래도 ListView가 손에 익다보니 쉽고 빠르게 하다보니  RecyclerView는 진짜 꼭 필요할 때만 사용하는데, 새로 나온 걸 좋아하는 성격상 뭔가 마음 속 한 구석이 불편하다. 성능도 더 좋아졌고, 커스터마이징하기도 좋은데, 왜 손이 안 가는지 모르겠다.  이번에 하는 프로젝트는 ListView를 쓰지않고 모두 다 RecyclerView를 써봐야겠다.

 

 

먼저 관련 라이브러리를 추가한다.

compile 'com.android.support:design:25.3.1'

 

RecyclerView는 기본적으로 행 구분선이 없기 때문에 데코레이터를 추가해줘야 한다.

public class DividerLineDecoratorViewModel implements BaseViewModel{
  public final ObservableField<DividerItemDecoration> decorator = new ObservableField<>();
  public Context context;

  @Override
  public void onCreate() {
    decorator.set(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL));
  }

  @Override
  public void onResume() {}

  @Override
  public void onPause() {}

  @Override
  public void onDestroy() {}
}

 

 

화면을 그린다.

여기에는 이전의 ListView 예제에서 사용한 UsersViewModel을 그대로 사용한다.

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

  <data>

    <variable
      name="model"
      type="kr.susemi99.testmvvm.list_view.UsersViewModel"/>

    <variable
      name="decorator"
      type="kr.susemi99.testmvvm.recycler_view.DividerLineDecoratorViewModel"/>
  </data>

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

    <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:onClick="@{()-> model.newUser()}"
      android:text="add"/>

    <android.support.v7.widget.RecyclerView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:scrollbars="vertical"
      app:items="@{model.users}"
      app:decorator="@{decorator.decorator}"
      app:layoutManager="LinearLayoutManager"/>
  </LinearLayout>
</layout>

만약 레이아웃에서 RecyclerView의 방향을 Vertical이 아닌 Horizontal로 하고 싶을 땐 어떻게 해야하는지는 못 찾았다.

 

 

 

RecyclerView의 한 행을 표시할 때 사용할 뷰홀더를 만들어 준다.

public class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
  private final T binding;

  public BindingViewHolder(View view) {
    super(view);
    this.binding = (T) DataBindingUtil.bind(view);
  }

  public T binding() {
    return binding;
  }
}

이 클래스는 다른 RecyclerView에서도 바로 사용할 수 있다.

 

 

RecyclerView에서 사용할 어댑터를 만든다.

public class UserRecyclerViewAdapter extends RecyclerView.Adapter<BindingViewHolder<ListItemUserBinding>> {
  private ArrayList<UserViewModel> users = new ArrayList<>();

  public void add(ArrayList<UserViewModel> users) {
    for (UserViewModel user : users) {
      if (!this.users.contains(user)) {
        this.users.add(user);
        notifyItemInserted(this.users.size() - 1);
      }
    }
  }

  @Override
  public BindingViewHolder<ListItemUserBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    return new BindingViewHolder<>(inflater.inflate(R.layout.list_item_user, parent, false));
  }

  @Override
  public void onBindViewHolder(BindingViewHolder<ListItemUserBinding> holder, int position) {
    holder.binding().setUser(users.get(position));
    holder.binding().setNavigator(navigator);
  }

  @Override
  public int getItemCount() {
    return users.size();
  }
}

ListView처럼 notifyDataSetChanged() 를 호출하면 화면이 번쩍거리니까 반드시 notifyItemInserted() 를 호출해야 한다.

 

 

 

액티비티에서는 어댑터와 데코레이터에 관한 정보를 받아서 처리한다.

public class RecyclerActivity extends AppCompatActivity {
  private UsersViewModel usersViewModel = new UsersViewModel();
  private DividerLineDecoratorViewModel decoratorViewModel = new DividerLineDecoratorViewModel();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityRecyclerBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler);
    binding.setModel(usersViewModel);
    binding.setDecorator(decoratorViewModel);
    decoratorViewModel.context = getApplicationContext();
    decoratorViewModel.onCreate();
  }

  @BindingAdapter("app:items")
  public static void setUserList(RecyclerView recyclerView, ArrayList<UserViewModel> users) {
    UserRecyclerViewAdapter adapter;

    if(recyclerView.getAdapter() == null) {
      adapter = new UserRecyclerViewAdapter();
      recyclerView.setAdapter(adapter);
    } else {
      adapter = (UserRecyclerViewAdapter) recyclerView.getAdapter();
    }

    adapter.add(users);
  }

  @BindingAdapter("app:decorator")
  public static void setDecorator(RecyclerView recyclerView, ObservableField<DividerItemDecoration> decorator) {
    recyclerView.addItemDecoration(decorator.get());
  }
}

 

 

이런 식으로 잘 나온다.