Viblo Learning
+1

RecyclerView-GridLayout: Header, Variable span size, AutoFit, ...

Trên phiên bản Android mới nhất ( Android L 5.0 ) google giới thiệu cho chúng ta một api mới rất thú vị là RecyclerView thay thế cho listview và gridview truyền thống.

Tôi xin giới thiệu cho các bạn cách sử dụng và một số thay đổi trên nó.

Trong ví dụ tôi tạo 1 class adapter đơn giản như sau
public class NumberedAdapter extends RecyclerView.Adapter<TextViewHolder> {
  private List<String> labels;

  public NumberedAdapter(int count) {
    labels = new ArrayList<String>(count);
    for (int i = 0; i < count; ++i) {
      labels.add(String.valueOf(i));
    }
  }

  @Override
  public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
    return new TextViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final TextViewHolder holder, final int position) {
    final String label = labels.get(position);
    holder.textView.setText(label);
    holder.textView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(
            holder.textView.getContext(), label, Toast.LENGTH_SHORT).show();
      }
    });
  }

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

I. Grid Layout Manager

Tạo 1 danh sách kiểu Gridview truyền thống. Trong ví dụ này tôi tạo 1 gridview với 2 cột.

javaRecyclerView recyclerView = (RecyclerView) findViewById(
    R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerView.setAdapter(new NumberedAdapter(30));

1.png

II. Variable span size

Với GridLayoutManager, mặc định các view sẽ có độ rộng kích thước bằng nhau nhưng chúng ta có thể dễ dàng thay đổi độ rộng kích thước các phần tử. Ở đoạn code dưới trên tôi thiết đặt độ rộng các view lần lượt là : 3 2 1 3 2 1 ... Các bạn xem code để hiểu rõ hơn cơ chế làm việc.

GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  @Override
  public int getSpanSize(int position) {
    return (3 - position % 3);
  }
});
recyclerView.setLayoutManager(manager);

2.png

III. Header

Không giống như Listview hay Gridview truyền thống, recyclerview không có điểm đầu và điểm cuối nhưng chúng ta vẫn có thể thêm header cho nó rất dễ dàng.

Trước tiên chúng ta thay đổi class adapter cơ bản như sau

public class HeaderNumberedAdapter extends RecyclerView.Adapter<TextViewHolder> {
  private static final int ITEM_VIEW_TYPE_HEADER = 0;
  private static final int ITEM_VIEW_TYPE_ITEM = 1;

  private final View header;
  private final List<String> labels;

  public HeaderNumberedAdapter(View header, int count) {
    if (header == null) {
      throw new IllegalArgumentException("header may not be null");
    }
    this.header = header;
    this.labels = new ArrayList<String>(count);
    for (int i = 0; i < count; ++i) {
      labels.add(String.valueOf(i));
    }
  }

  public boolean isHeader(int position) {
    return position == 0;
  }

  @Override
  public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == ITEM_VIEW_TYPE_HEADER) {
      return new TextViewHolder(header);
    }
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
    return new TextViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final TextViewHolder holder, final int position) {
    if (isHeader(position)) {
      return;
    }
    final String label = labels.get(position - 1);  // Subtract 1 for header
    holder.textView.setText(label);
    holder.textView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(
            holder.textView.getContext(), label, Toast.LENGTH_SHORT).show();
      }
    });
  }

  @Override
  public int getItemViewType(int position) {
    return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
  }

  @Override
  public int getItemCount() {
    return labels.size() + 1;
  }
}

Trong activity

final GridLayoutManager manager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(manager);

View header = LayoutInflater.from(this).inflate(
    R.layout.header, recyclerView, false);
header.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Toast.makeText(v.getContext(), R.string.grid_layout_header,
        Toast.LENGTH_SHORT).show();
  }
});
final HeaderNumberedAdapter adapter
    = new HeaderNumberedAdapter(header, 30);
recyclerView.setAdapter(adapter);

manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  @Override
  public int getSpanSize(int position) {
    return adapter.isHeader(position) ? manager.getSpanCount() : 1;
  }
});

3.png

IV. Autofit RecyclerView

Không giống như GridView, việc thiết lập autofit cho các phần tử trong recyclerView phức tạp hơn 1 chút.

private void init(Context context, AttributeSet attrs) {
  if (attrs != null) {
    int[] attrsArray = {
        android.R.attr.columnWidth
    };
    TypedArray array = context.obtainStyledAttributes(
      attrs, attrsArray);
    columnWidth = array.getDimensionPixelSize(0, -1);
    array.recycle();
  }

  manager = new GridLayoutManager(getContext(), 1);
  setLayoutManager(manager);
}
protected void onMeasure(int widthSpec, int heightSpec) {
  super.onMeasure(widthSpec, heightSpec);
  if (columnWidth > 0) {
    int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
    manager.setSpanCount(spanCount);
  }
}

Màn hình dọc

4.png

Màn hình ngang

5.png

V. Project Demo

Project được build với Android Studio.

Link

http://www.fshare.vn/file/SCZQGPYDQB4D

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.