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 ...
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ư saupublic 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));
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);
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; } });
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 awidthSpec, int heightSpec) { super.onMeasure(awidthSpec, heightSpec); if (columnWidth > 0) { int spanCount = Math.max(1, getMeasuredWidth() / columnWidth); manager.setSpanCount(spanCount); } }
Màn hình dọc
Màn hình ngang
V. Project Demo
Project được build với Android Studio.
Link
http://www.fshare.vn/file/SCZQGPYDQB4D