Android 5.0 – 添加页眉/页脚到RecyclerView

我花了一些时间试图找出一个方法来添加一个头到一个RecyclerView ,失败。 这是我到目前为止:

 @Override protected void onCreate(Bundle savedInstanceState) { ... layouManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layouManager); LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); headerPlaceHolder = inflater.inflate(R.layout.view_header_holder_medium, null, false); layouManager.addView(headerPlaceHolder, 0); ... } 

LayoutManager似乎是处理RecyclerView项目处置的对象。 由于找不到任何addHeaderView(View view)方法,我决定使用LayoutManageraddView(View view, int position)方法,并在第一个位置添加我的标题视图以像标题一样工作。

Aaand这是更丑陋的地方:

 java.lang.NullPointerException: Attempt to read from field 'android.support.v7.widget.RecyclerView$ViewHolder android.support.v7.widget.RecyclerView$LayoutParams.mViewHolder' on a null object reference at android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.java:2497) at android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:4807) at android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:4803) at com.mathieumaree.showz.fragments.CategoryFragment.setRecyclerView(CategoryFragment.java:231) at com.mathieumaree.showz.fragments.CategoryFragment.access$200(CategoryFragment.java:47) at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:201) at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.java:196) at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:41) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 

在创buildActivity的不同时刻,尝试调用addView(View view)之后,有几个NullPointerExceptions (也尝试添加视图,一旦设置了所有内容,即使是Adapter的数据),我意识到我不知道这是否是正确的方法做到这一点(而且看起来不是这样)。

PS:此外,除了LinearLayoutManager之外,还可以处理GridLayoutManager的解决scheme,我们将非常感激!

find一个关于这个https://plus.google.com/+WillBlaschko/posts/3MFmgPbQuWx非常好的文章;

我不得不添加一个页脚到我的RecyclerView ,在这里我分享我的代码片段,因为我认为它可能是有用的。

 public class RecentCallsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int FOOTER_VIEW = 1; // Define a view holder for Footer view public class FooterViewHolder extends ViewHolder { public FooterViewHolder(View itemView) { super(itemView); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Do whatever you want on clicking the item } }); } } // Now define the viewholder for Normal list item public class NormalViewHolder extends ViewHolder { public NormalViewHolder(View itemView) { super(itemView); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Do whatever you want on clicking the normal items } }); } } // And now in onCreateViewHolder you have to pass the correct view // while populating the list item. @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v; if (viewType == FOOTER_VIEW) { v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false); FooterViewHolder vh = new FooterViewHolder(v); return vh; } v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_normal, parent, false); NormalViewHolder vh = new NormalViewHolder(v); return vh; } // Now bind the viewholders in onBindViewHolder @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { try { if (holder instanceof NormalViewHolder) { NormalViewHolder vh = (NormalViewHolder) holder; vh.bindView(position); } else if (holder instanceof FooterViewHolder) { FooterViewHolder vh = (FooterViewHolder) holder; } } catch (Exception e) { e.printStackTrace(); } } // Now the critical part. You have return the exact item count of your list // I've only one footer. So I returned data.size() + 1 // If you've multiple headers and footers, you've to return total count // like, headers.size() + data.size() + footers.size() @Override public int getItemCount() { if (data == null) { return 0; } if (data.size() == 0) { //Return 1 here to show nothing return 1; } // Add extra view to show the footer view return data.size() + 1; } // Now define getItemViewType of your own. @Override public int getItemViewType(int position) { if (position == data.size()) { // This is where we'll add footer. return FOOTER_VIEW; } return super.getItemViewType(position); } // So you're done with adding a footer and its action on onClick. // Now set the default ViewHolder for NormalViewHolder public class ViewHolder extends RecyclerView.ViewHolder { // Define elements of a row here public ViewHolder(View itemView) { super(itemView); // Find view by ID and initialize here } public void bindView(int position) { // bindView() method to implement actions } } } 

我在棒棒糖上遇到了同样的问题,并创build了两种方法来包装Recyclerview适配器。 一个是很容易使用,但我不知道它将如何改变数据集。 因为它包装了你的适配器,你需要确保在正确的适配器对象上调用像notifyDataSetChanged这样的方法。

另一个不应该有这样的问题。 只要让你的普通适配器扩展类,实现抽象方法,你应该准备好了。 在这里他们是:

学家

  • HeaderRecyclerViewAdapterV1.java用法new HeaderRecyclerViewAdapterV1(new RegularAdapter());
  • HeaderRecyclerViewAdapterV2.java用法RegularAdapter extends HeaderRecyclerViewAdapterV2

HeaderRecyclerViewAdapterV1

 import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; /** * Created by sebnapi on 08.11.14. * <p/> * This is a Plug-and-Play Approach for adding a Header or Footer to * a RecyclerView backed list * <p/> * Just wrap your regular adapter like this * <p/> * new HeaderRecyclerViewAdapterV1(new RegularAdapter()) * <p/> * Let RegularAdapter implement HeaderRecyclerView, FooterRecyclerView or both * and you are ready to go. * <p/> * I'm absolutely not sure how this will behave with changes in the dataset. * You can always wrap a fresh adapter and make sure to not change the old one or * use my other approach. * <p/> * With the other approach you need to let your Adapter extend HeaderRecyclerViewAdapterV2 * (and therefore change potentially more code) but possible omit these shortcomings. * <p/> * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :) */ public class HeaderRecyclerViewAdapterV1 extends RecyclerView.Adapter { private static final int TYPE_HEADER = Integer.MIN_VALUE; private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1; private static final int TYPE_ADAPTEE_OFFSET = 2; private final RecyclerView.Adapter mAdaptee; public HeaderRecyclerViewAdapterV1(RecyclerView.Adapter adaptee) { mAdaptee = adaptee; } public RecyclerView.Adapter getAdaptee() { return mAdaptee; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_HEADER && mAdaptee instanceof HeaderRecyclerView) { return ((HeaderRecyclerView) mAdaptee).onCreateHeaderViewHolder(parent, viewType); } else if (viewType == TYPE_FOOTER && mAdaptee instanceof FooterRecyclerView) { return ((FooterRecyclerView) mAdaptee).onCreateFooterViewHolder(parent, viewType); } return mAdaptee.onCreateViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == 0 && holder.getItemViewType() == TYPE_HEADER && useHeader()) { ((HeaderRecyclerView) mAdaptee).onBindHeaderView(holder, position); } else if (position == mAdaptee.getItemCount() && holder.getItemViewType() == TYPE_FOOTER && useFooter()) { ((FooterRecyclerView) mAdaptee).onBindFooterView(holder, position); } else { mAdaptee.onBindViewHolder(holder, position - (useHeader() ? 1 : 0)); } } @Override public int getItemCount() { int itemCount = mAdaptee.getItemCount(); if (useHeader()) { itemCount += 1; } if (useFooter()) { itemCount += 1; } return itemCount; } private boolean useHeader() { if (mAdaptee instanceof HeaderRecyclerView) { return true; } return false; } private boolean useFooter() { if (mAdaptee instanceof FooterRecyclerView) { return true; } return false; } @Override public int getItemViewType(int position) { if (position == 0 && useHeader()) { return TYPE_HEADER; } if (position == mAdaptee.getItemCount() && useFooter()) { return TYPE_FOOTER; } if (mAdaptee.getItemCount() >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) { new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + "."); } return mAdaptee.getItemViewType(position) + TYPE_ADAPTEE_OFFSET; } public static interface HeaderRecyclerView { public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType); public void onBindHeaderView(RecyclerView.ViewHolder holder, int position); } public static interface FooterRecyclerView { public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType); public void onBindFooterView(RecyclerView.ViewHolder holder, int position); } } 

HeaderRecyclerViewAdapterV2

 import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; /** * Created by sebnapi on 08.11.14. * <p/> * If you extend this Adapter you are able to add a Header, a Footer or both * by a similar ViewHolder pattern as in RecyclerView. * <p/> * If you want to omit changes to your class hierarchy you can try the Plug-and-Play * approach HeaderRecyclerViewAdapterV1. * <p/> * Don't override (Be careful while overriding) * - onCreateViewHolder * - onBindViewHolder * - getItemCount * - getItemViewType * <p/> * You need to override the abstract methods introduced by this class. This class * is not using generics as RecyclerView.Adapter make yourself sure to cast right. * <p/> * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :) */ public abstract class HeaderRecyclerViewAdapterV2 extends RecyclerView.Adapter { private static final int TYPE_HEADER = Integer.MIN_VALUE; private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1; private static final int TYPE_ADAPTEE_OFFSET = 2; @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_HEADER) { return onCreateHeaderViewHolder(parent, viewType); } else if (viewType == TYPE_FOOTER) { return onCreateFooterViewHolder(parent, viewType); } return onCreateBasicItemViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == 0 && holder.getItemViewType() == TYPE_HEADER) { onBindHeaderView(holder, position); } else if (position == getBasicItemCount() && holder.getItemViewType() == TYPE_FOOTER) { onBindFooterView(holder, position); } else { onBindBasicItemView(holder, position - (useHeader() ? 1 : 0)); } } @Override public int getItemCount() { int itemCount = getBasicItemCount(); if (useHeader()) { itemCount += 1; } if (useFooter()) { itemCount += 1; } return itemCount; } @Override public int getItemViewType(int position) { if (position == 0 && useHeader()) { return TYPE_HEADER; } if (position == getBasicItemCount() && useFooter()) { return TYPE_FOOTER; } if (getBasicItemType(position) >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) { new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + "."); } return getBasicItemType(position) + TYPE_ADAPTEE_OFFSET; } public abstract boolean useHeader(); public abstract RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType); public abstract void onBindHeaderView(RecyclerView.ViewHolder holder, int position); public abstract boolean useFooter(); public abstract RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType); public abstract void onBindFooterView(RecyclerView.ViewHolder holder, int position); public abstract RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType); public abstract void onBindBasicItemView(RecyclerView.ViewHolder holder, int position); public abstract int getBasicItemCount(); /** * make sure you don't use [Integer.MAX_VALUE-1, Integer.MAX_VALUE] as BasicItemViewType * * @param position * @return */ public abstract int getBasicItemType(int position); } 

反馈和叉赞赏。 我将自己使用HeaderRecyclerViewAdapterV2进行testing,并在将来发布更改。

编辑 :@OvidiuLatcu是的,我有一些问题。 其实我停止偏移头position - (useHeader() ? 1 : 0) ,而是为它创build一个公共方法int offsetPosition(int position) 。 因为如果你在Recyclerview上设置了一个OnItemTouchListener ,你可以截取触摸,获得触摸的x,y坐标,find相应的子视图,然后调用recyclerView.getChildPosition(...) ,你将永远得到无偏移在适配器的位置! 这是RecyclerView代码中的一个缺陷,我没有看到一个简单的方法来克服这一点。 这就是为什么我现在用我自己的代码来明确地抵销这些职位的原因。

很简单解决!

我不喜欢将适配器内部的逻辑视为不同的视图types,因为每次在返回视图之前检查视图types。 以下解决scheme避免了额外的检查

只需在android.support.v4.widget.NestedScrollView中添加LinearLayout(垂直)标题视图+ recyclerview +页脚视图即可。

看一下这个:

  <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <View android:id="@+id/header" android:layout_width="match_parent" android:layout_height="wrap_content"/> <android.support.v7.widget.RecyclerView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="wrap_content" app:layoutManager="LinearLayoutManager"/> <View android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </android.support.v4.widget.NestedScrollView> 

添加这行代码平滑滚动

 RecyclerView v = (RecyclerView) findViewById(...); v.setNestedScrollingEnabled(false); 

PS这只有当你的清单非常小(例如导航抽屉,设置等)时才有用,因为你可能会失去RecyclerView的再循环优势。

我还没有尝试过,但我只是简单地添加1(或2,如果你想要一个页眉和页脚)的getItemCount在您的适配器返回的整数。 然后你可以覆盖getItemViewType在你的适配器来返回一个不同的整数,当i==0 : https : //developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType( i==0

然后, createViewHolder传递从getItemViewType返回的整数,允许您为标题视图创build或configuration不同的视图持有者: https : //developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter。 html#createViewHolder(android.view.ViewGroup ,int)

不要忘记从传递给bindViewHolder的位置整数中减去一个。

你可以使用这个GitHub库,允许在你的RecyclerView中以最简单的方式添加Header和/或Footer

您需要在您的项目中添加HFRecyclerView库,或者您也可以从Gradle中抓取它:

 compile 'com.mikhaellopez:hfrecyclerview:1.0.0' 

这是形象的结果:

预习

编辑:

如果你只是想在这个库的顶部和/或底部添加一个边距: SimpleItemDecoration :

 int offsetPx = 10; recyclerView.addItemDecoration(new StartOffsetItemDecoration(offsetPx)); recyclerView.addItemDecoration(new EndOffsetItemDecoration(offsetPx)); 

我结束了实现我自己的适配器来包装任何其他适配器,并提供方法来添加页眉和页脚视图。

在这里创build一个要点: HeaderViewRecyclerAdapter.java

我想要的主要function是与ListView类似的接口,所以我想能够在我的Fragment中膨胀视图,并将它们添加到onCreateViewRecyclerView 。 这是通过创build一个HeaderViewRecyclerAdapter传递适配器来完成的,然后调用addHeaderViewaddFooterView传递你的充气视图。 然后将HeaderViewRecyclerAdapter实例设置为RecyclerView上的适配器。

另一个要求是我需要能够在保留页眉和页脚的情况下轻松地更换适配器,我不想让多个适配器具有这些页眉和页脚的多个实例。 因此,您可以调用setAdapter来更改包装的适配器,使标题和页脚完好无损,并将RecyclerView通知更改。

基于@ seb的解决scheme,我创build了一个RecyclerView.Adapter的子类,支持任意数量的页眉和页脚。

https://gist.github.com/mheras/0908873267def75dc746

虽然它似乎是一个解决scheme,我也认为这件事情应该由LayoutManagerpipe理。 不幸的是,我现在需要它,我没有时间从头开始实现一个StaggeredGridLayoutManager(甚至没有扩展它)。

我仍在testing它,但是如果你愿意,你可以试试。 请让我知道,如果你发现任何问题。

您可以使用viewtype来解决这个问题,这里是我的演示: https : //github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView

  1. 您可以定义一些回收站视图显示模式:

    public static final int MODE_DATA = 0,MODE_LOADING = 1,MODE_ERROR = 2,MODE_EMPTY = 3,MODE_HEADER_VIEW = 4,MODE_FOOTER_VIEW = 5;

2.重写getItemViewType方法

  @Override public int getItemViewType(int position) { if (mMode == RecyclerViewMode.MODE_LOADING) { return RecyclerViewMode.MODE_LOADING; } if (mMode == RecyclerViewMode.MODE_ERROR) { return RecyclerViewMode.MODE_ERROR; } if (mMode == RecyclerViewMode.MODE_EMPTY) { return RecyclerViewMode.MODE_EMPTY; } //check what type our position is, based on the assumption that the order is headers > items > footers if (position < mHeaders.size()) { return RecyclerViewMode.MODE_HEADER_VIEW; } else if (position >= mHeaders.size() + mData.size()) { return RecyclerViewMode.MODE_FOOTER_VIEW; } return RecyclerViewMode.MODE_DATA; } 

3.重写getItemCount方法

 @Override public int getItemCount() { if (mMode == RecyclerViewMode.MODE_DATA) { return mData.size() + mHeaders.size() + mFooters.size(); } else { return 1; } } 

4.覆盖onCreateViewHolder方法。 通过viewType创build视图持有者

 @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == RecyclerViewMode.MODE_LOADING) { RecyclerView.ViewHolder loadingViewHolder = onCreateLoadingViewHolder(parent); loadingViewHolder.itemView.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight) ); return loadingViewHolder; } if (viewType == RecyclerViewMode.MODE_ERROR) { RecyclerView.ViewHolder errorViewHolder = onCreateErrorViewHolder(parent); errorViewHolder.itemView.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight) ); errorViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (null != mOnErrorViewClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnErrorViewClickListener.onErrorViewClick(v); } }, 200); } } }); return errorViewHolder; } if (viewType == RecyclerViewMode.MODE_EMPTY) { RecyclerView.ViewHolder emptyViewHolder = onCreateEmptyViewHolder(parent); emptyViewHolder.itemView.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight) ); emptyViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (null != mOnEmptyViewClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnEmptyViewClickListener.onEmptyViewClick(v); } }, 200); } } }); return emptyViewHolder; } if (viewType == RecyclerViewMode.MODE_HEADER_VIEW) { RecyclerView.ViewHolder headerViewHolder = onCreateHeaderViewHolder(parent); headerViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (null != mOnHeaderViewClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnHeaderViewClickListener.onHeaderViewClick(v, v.getTag()); } }, 200); } } }); return headerViewHolder; } if (viewType == RecyclerViewMode.MODE_FOOTER_VIEW) { RecyclerView.ViewHolder footerViewHolder = onCreateFooterViewHolder(parent); footerViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (null != mOnFooterViewClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnFooterViewClickListener.onFooterViewClick(v, v.getTag()); } }, 200); } } }); return footerViewHolder; } RecyclerView.ViewHolder dataViewHolder = onCreateDataViewHolder(parent); dataViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if (null != mOnItemClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnItemClickListener.onItemClick(v, v.getTag()); } }, 200); } } }); dataViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(final View v) { if (null != mOnItemLongClickListener) { new Handler().postDelayed(new Runnable() { @Override public void run() { mOnItemLongClickListener.onItemLongClick(v, v.getTag()); } }, 200); return true; } return false; } }); return dataViewHolder; } 

5.覆盖onBindViewHolder方法。 通过viewType绑定数据

 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (mMode == RecyclerViewMode.MODE_LOADING) { onBindLoadingViewHolder(holder, position); } else if (mMode == RecyclerViewMode.MODE_ERROR) { onBindErrorViewHolder(holder, position); } else if (mMode == RecyclerViewMode.MODE_EMPTY) { onBindEmptyViewHolder(holder, position); } else { if (position < mHeaders.size()) { if (mHeaders.size() > 0) { onBindHeaderViewHolder(holder, position); } } else if (position >= mHeaders.size() + mData.size()) { if (mFooters.size() > 0) { onBindFooterViewHolder(holder, position - mHeaders.size() - mData.size()); } } else { onBindDataViewHolder(holder, position - mHeaders.size()); } } } 

我知道我来晚了,但直到最近我才能够实现这样的“addHeader”适配器。 在我的FlexibleAdapter项目中,你可以调用一个可分区项目上的setHeader ,然后调用showAllHeaders 。 如果你只需要1个标题,那么第一个项目应该有标题。 如果删除这个项目,那么标题会自动链接到下一个。

不幸的是,页脚没有被覆盖(还)。

FlexibleAdapter允许你做更多的事情比创build标题/部分。 你真的应该看看: https : //github.com/davideas/FlexibleAdapter 。

您可以使用库SectionedRecyclerViewAdapter将项目分组,并为每个部分添加一个标题,如下图所示:

在这里输入图像说明

首先你创build你的部分课程:

 class MySection extends StatelessSection { String title; List<String> list; public MySection(String title, List<String> list) { // call constructor with layout resources for this Section header, footer and items super(R.layout.section_header, R.layout.section_item); this.title = title; this.list = list; } @Override public int getContentItemsTotal() { return list.size(); // number of items of this section } @Override public RecyclerView.ViewHolder getItemViewHolder(View view) { // return a custom instance of ViewHolder for the items of this section return new MyItemViewHolder(view); } @Override public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) { MyItemViewHolder itemHolder = (MyItemViewHolder) holder; // bind your view here itemHolder.tvItem.setText(list.get(position)); } @Override public RecyclerView.ViewHolder getHeaderViewHolder(View view) { return new SimpleHeaderViewHolder(view); } @Override public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) { MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder; // bind your header view here headerHolder.tvItem.setText(title); } } 

然后,用您的部分设置RecyclerView,并使用GridLayoutManager更改标题的SpanSize:

 // Create an instance of SectionedRecyclerViewAdapter SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter(); // Create your sections with the list of data MySection section1 = new MySection("My Section 1 title", dataList1); MySection section2 = new MySection("My Section 2 title", dataList2); // Add your Sections to the adapter sectionAdapter.addSection(section1); sectionAdapter.addSection(section2); // Set up a GridLayoutManager to change the SpanSize of the header GridLayoutManager glm = new GridLayoutManager(getContext(), 2); glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { switch(sectionAdapter.getSectionItemViewType(position)) { case SectionedRecyclerViewAdapter.VIEW_TYPE_HEADER: return 2; default: return 1; } } }); // Set up your RecyclerView with the SectionedRecyclerViewAdapter RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); recyclerView.setLayoutManager(glm); recyclerView.setAdapter(sectionAdapter); 

我只是添加一个替代所有这些HeaderRecyclerViewAdapter实现。 CompoundAdapter:

https://github.com/negusoft/CompoundAdapter-android

这是一种更灵活的方法,因为您可以从适配器创buildAdapterGroup。 对于头部示例,请按原样使用您的适配器以及包含一个头部项目的适配器:

 AdapterGroup adapterGroup = new AdapterGroup(); adapterGroup.addAdapter(SingleAdapter.create(R.layout.header)); adapterGroup.addAdapter(new MyAdapter(...)); recyclerView.setAdapter(adapterGroup); 

这是相当简单和可读。 您可以使用相同的原理轻松实现更复杂的适配器。