12/08/2018, 16:57

Tạo Recyclerview adapters awesome với MultiViewAdapter

Hiện tại Recyclerview đã trở thành một widget quan trọng và phần lớn các app Android đều sử dụng. Nó là một công cụ mạnh mẽ, rất tiện lợi và có thể cover hầu hết các case mà lập trình viên muốn. Tuy nhiên vì sự linh hoạt của nó nên lập trình viên có thể vấp phải một vài khó khăn khi tạo ...

Hiện tại Recyclerview đã trở thành một widget quan trọng và phần lớn các app Android đều sử dụng. Nó là một công cụ mạnh mẽ, rất tiện lợi và có thể cover hầu hết các case mà lập trình viên muốn. Tuy nhiên vì sự linh hoạt của nó nên lập trình viên có thể vấp phải một vài khó khăn khi tạo adapter. Như chúng ta đã biết, Recyclerview hỗ trợ nhiều kiểu view, điều này là lợi thế lớn so với ListView, tuy nhiên việc hiển thị nhiểu kiểu view sẽ tạo ra nhiều boilerplate code. Với hơn 3 kiểu view bạn có thể có nhiều if-else, switch-case. Những giải pháp có sẵn gặp một vài hạn chế như sau:

  • Đối tượng dữ liệu của bạn phải chung một common parent, có thể can thiệp vào mô hình đối tượng của bạn.
  • Bạn buộc phải giữ ID resource layout bên trong lớp model, điều này phá vỡ dependency hierarchy .
  • Không thể sử dụng cùng một layout file cho hai view type khác nhau. .... MultiViewAdapter giải quyết những vấn đề này. Cùng kiểm tra xem nhé.

Source code: https://github.com/DevAhamed/MultiViewAdapter

  • Không có hạn chế về mô hình hóa và phân lớp đối tượng
  • Hỗ trợ DiffUtil
  • Hỗ trợ single selection và multi-selection
  • Tách riêng mỗi view type, bạn không cần phải switch-case hay if-else nữa
dependencies {
	// ... other dependencies here
    compile 'com.github.devahamed:multi-view-adapter:1.1.0'
}

  1. RecyclerAdapter: đây là lớp adapter, có nhiều ItemBinder và DataManager. Nó được extends từ RecyclerView.Adapter.
  2. ItemBinder: khởi tạo và bind các ViewHolder, ItemBinder có kiểu tham số phù hợp với mỗi model class cần hiển thị. Nó cần phải được đăng ký bên trong RecyclerAdapter. ItemBinder có thể đăng ký với nhiều adapter.
  3. DataManager: Nó giữ data và gọi các animation cần thiết khi data-set thay đổi. Có hai kiểu là: DataListManager cho list item và DataItemManager cho single item (Header, Footer etc.).

Bạn có một list đối tượng, gọi là Car, bạn muốn hiển thị list Car:

public class CarAdapter extends RecyclerAdapter {

  private DataListManager<CarModel> dataManager;

  public CarAdapter() {
    this.dataManager = new DataListManager<>(this);
    addDataManager(dataManager);

    registerBinder(new CarBinder());
  }

  public void addData(List<CarModel> dataList) {
    dataManager.addAll(dataList);
  }
}
class CarBinder extends ItemBinder<CarModel, CarBinder.CarViewHolder> {

  @Override public CarViewHolder create(LayoutInflater inflater, ViewGroup parent) {
    return new CarViewHolder(inflater.inflate(R.layout.item_car, parent, false));
  }

  @Override public boolean canBindData(Object item) {
    return item instanceof CarModel;
  }

  @Override public void bind(CarViewHolder holder, CarModel item) {
    // Bind the data here
  }

  static class CarViewHolder extends BaseViewHolder<ItemModel> {
    // Normal ViewHolder code
  }
}

Ở đây ta cần chú ý với cách cũ, ta luôn cần phải tạo một single class CarAdapter. Nhưng khi sử dụng lib này, bạn phải tạo 2 class là CarAdapter và CarBinder, mục đích là bạn có thể sử dụng lại CarBinder cho một adapter khác.

Khi hiển thị một gridlayout, bạn chỉ cần override hàm getSpanSize(int maxSpanCount) bên trong ItemBinder class và trả về span count.

class CarBinder extends ItemBinder<CarModel, CarBinder.CarViewHolder> {

  // Rest of the code
  
  @Override public int getSpanSize(int maxSpanCount) {
    return 1; // Return any number which is less than maxSpanCount 
  }
}

  1. Tạo lớp item custom được extens từ ItemDecoration
public class MyItemDecorator implements ItemDecorator {

  public MyItemDecorator() {
    // Any initialization code
  }

  @Override public void getItemOffsets(Rect outRect, int position, int positionType) {
    // Set item offset for each item
    // outRect.set(0, 0, 0, 0);
  }

  @Override public void onDraw(Canvas canvas, RecyclerView parent, View child, int position,
      int positionType) {
    // Canvas drawing code implementation
    // Unlike default ItemDecoration, this method will be called for individual items. Do not create objects here.
  }
}
  1. Tạo Custom Binder, truyền đối tượng item custom mới tạo ở trên vào constructor
public class CustomItemBinder implements ItemBinder {

  public CustomItemBinder(CustomItemDecorator customItemDecorator) {
    super(customItemDecorator);
  }
}
  1. Cuối cùng là get item từ adapter và add vào recyclerview
private void initRecyclerView() {
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rcv);
    MyAdapter adapter = new MyAdapter();

    recyclerView.addItemDecoration(adapter.getItemDecorationManager());
    recyclerView.setAdapter(adapter);
}

MultiViewAdapter hỗ trợ 3 kiểu selection :

  1. Chọn một lần - Chỉ có thể chọn một mục. Khi một mục được chọn, nó không thể được bỏ chọn trừ khi một mục khác được chọn.
  2. Lựa chọn một hoặc không có - Chỉ có một mục có thể được chọn. Một mục có thể được bỏ chọn bằng cách thực hiện thao tác lựa chọn tương tự.
  3. Đa lựa chọn - Nhiều mục có thể được lựa chọn trên các DataManagers khác nhau. Bạn cần phải sử dụng Selectable trong Adapter, ItemBinder, và ViewHolder
public class CarAdapter extends SelectableAdapter {

  private DataListManager<CarModel> dataManager;

  public CarAdapter() {
    setSelectionMode(SELECTION_MODE_SINGLE);
    
    this.dataManager = new DataListManager<>(this);
    addDataManager(dataManager);

    registerBinder(new CarBinder());
  }

  public void addData(List<CarModel> dataList) {
    dataManager.addAll(dataList);
  }
}
class CarBinder extends SelectableBinder<CarModel, CarBinder.CarViewHolder> {

  @Override public CarViewHolder create(LayoutInflater inflater, ViewGroup parent) {
    return new CarViewHolder(inflater.inflate(R.layout.item_car, parent, false));
  }

  @Override public boolean canBindData(Object item) {
    return item instanceof CarModel;
  }

  @Override public void bind(CarViewHolder holder, CarModel item, boolean isSelected) {
    // Bind the data here
    // Whenever the selection status changes, this ethod will be called.
    // Use "isSelected" to know whether the item is selectable
  }
static class CarViewHolder extends SelectableViewHolder<CarModel> {
    
    CarViewHolder(View itemView) {
      super(itemView);

      setItemClickListener(new OnItemClickListener<CarModel>() {
        @Override public void onItemClick(View view, GridItem item) {
          itemSelectionToggled();
        }
      });
  }

ViewHolders có 2 listeners: OnItemClickListener và OnItemLongClickListener.

DataListManager có ItemSelectionChangedListener và MultiSelectionChangedListener. Các hàm này có thể được sử dụng với SelectableAdapter.

Còn rất nhiều thứ bạn có thể dùng với MultiViewAdapter như kéo thả, Swipe to dismiss, Infinite scrolling ,... Chúc các bạn code vui vẻ Nguồn

0