RecyclerView:實現帶header的grid
原文:RecyclerView: Grid with header
GridView和ListView有許多的相似之處,不過也有一個顯著的不同:沒有header和footer。現在它們兩者都可以用RecyclerView實現,我想看看如何在grid上添加header。
GridLayoutManager
我用GridLayoutManager創建了一個spanCount為2的RecylcerView。
注:spanCount即列數。這里GridLayoutManager的第二個參數就是spanCount。
RecyclerView recyclerView = (RecyclerView) findViewById( R.id.recycler_view); recyclerView.addItemDecoration(new MarginDecoration(this)); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); recyclerView.setAdapter(new NumberedAdapter(30));
NumberedAdapter 以字符串的形式顯示了每個item的position,在點擊的時候顯示一個toast。
可變的span size
在上面的基本設置中,我們的spanCount為2,每個item的span size為1,因此一個header需要的span size則為2。在我嘗試著添加header之前,我想先看看如何設置 span size。其實很簡單。
注:span size表示一個item的跨度,跨度了多少個span。
GridLayoutManager manager = new GridLayoutManager(this, 3); manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return (3 - position % 3); } }); recyclerView.setLayoutManager(manager);
setSpanSizeLookup可以讓你根據position來設置 span size,上面代碼中的公式所得到的 span size依次是3, 2, 1, 3, 2, 1...
GridLayoutVariableSpanSizeActivity.java
頭部(header)
現在讓我們來添加一個header!我們需要一個提供兩種view類型的adapter,一個為header一個為普通的item。可以看看HeaderNumberedAdapter,它在構造函數中把一個view作為header,然后把它存在一個成員變量中。
package com.sqisland.android.recyclerview; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Toast; import java.util.ArrayList; import java.util.List; 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; } }
其中TextViewHolder的代碼為:
package com.sqisland.android.recyclerview; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.TextView; public class TextViewHolder extends RecyclerView.ViewHolder { public TextView textView; public TextViewHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.text); } }
在RecyclerView新建一個view的時候,如果處于header的位置,我們用view holder來封裝這個header。onBindViewHolder中不必對header做任何事情,因為它的邏輯是在activity中處理的。
回到activity。我們需要用一個header來初始化HeaderNumberedAdapter,同時重寫setSpanSizeLookup,讓header橫跨所有列。
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; } });
我們inflate header,定義它的點擊事件,使用它去構造adapter。然后再setSpanSizeLookup中,我們在header所處的位置返回和span count (列數)相等的 span size。
總結
為了用RecyclerView創建一個帶header的grid:
-
定義一個具有兩種view類型的adapter,一個為header一個為普通item。
-
nflate一個header,把它傳遞給adapter。
-
重寫GridLayoutManager中的setSpanSizeLookup,在header所處的位置返回和span count (列數)相等的 span size。
源碼: https://github.com/chiuki/android-recyclerview
ps:在我的g+上有人評論說如果你不需要RecyclerView的功能,比如animation,reordering,staggering等,你也可以從AOSP中拷貝HeaderGridView.java。
來自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0722/3214.html