RecyclerView有一个addHeaderView等价物吗?

我正在寻找一个相当于addHeaderView回收视图。 基本上我想有一个图像与2个button被添加为列表视图的标题。 有没有不同的方式来添加一个标题视图到回收站视图? 一个指导的例子将是有帮助的

编辑2(添加片段布局):

添加日志语句后,似乎getViewType只能接收到0的位置。这导致onCreateView只加载一个布局:

10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemCount: 5 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> getItemViewType position: 0 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> onCreateViewHolder, viewtype: 0 10-26 16:32:53.766 5449-5449/co.testapp I/logger info﹕ Adapter-> onBindViewHolder, viewType: 0 

片段转换来加载CommentFragment:

 @Override public void onPhotoFeedItemClick(View view, int position) { if (fragmentManager == null) fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); if (view.getId() == R.id.button_comment){ CommentFragment commentFragment = CommentFragment.newInstance("","", position); fragmentTransaction.add(R.id.main_activity, commentFragment,"comment_fragment_tag"); fragmentTransaction.addToBackStack(Constants.TAG_COMMENTS); fragmentTransaction.commit(); } } 

片段的onCreateView:

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_comment, container, false); mRecyclerView = (RecyclerView) view.findViewById(R.id.list_recylclerview); mRecyclerView.setLayoutManager(new LinearLayoutManager(_context)); mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mAdapter = new CommentAdapter(R.layout.row_list_comments, R.layout.row_header_comments, _context, comments); mRecyclerView.setAdapter(mAdapter); return view; } 

包含recycleview的片段:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" tools:context="co.testapp.fragments.CommentFragment" android:background="@color/white"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list_recylclerview" android:layout_width="match_parent" android:layout_height="200dp" /> </RelativeLayout> </RelativeLayout> 

评论行布局:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" android:background="@color/white"> <!--Profile Picture--> <ImageView android:layout_width="80dp" android:layout_height="80dp" android:id="@+id/profile_picture" android:background="@color/blue_testapp"/> <!--Name--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="First Name Last Name" android:textSize="16dp" android:textColor="@color/blue_testapp" android:id="@+id/name_of_poster" android:layout_toRightOf="@id/profile_picture" /> <!--Comment--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" android:layout_marginTop="-5dp" android:text="This is a test comment" android:textSize="14dp" android:textColor="@color/black" android:id="@+id/comment" android:layout_below="@id/name_of_poster" android:layout_toRightOf="@id/profile_picture"/> </RelativeLayout> 

标题

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="wrap_content" android:layout_height="300dp" android:id="@+id/header_photo" android:layout_gravity="center_horizontal"/> </LinearLayout> 

适配器代码(感谢hister让我开始):

 public class CommentAdapter extends RecyclerView.Adapter<ViewHolder>{ private final int rowCardLayout; public static Context mContext; private final int headerLayout; private final String [] comments; private static final int HEADER = 0; private static final int OTHER = 0; public CommentAdapter(int rowCardLayout, int headerLayout, Context context, String [] comments) { this.rowCardLayout = rowCardLayout; this.mContext = context; this.comments = comments; this.headerLayout = headerLayout; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { logger.i("onCreateViewHolder, viewtype: " + i); //viewtype always returns 0 so OTHER layout is never inflated if (i == HEADER) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(headerLayout, viewGroup, false); return new ViewHolderHeader(v); } else if (i == OTHER){ View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowCardLayout, viewGroup, false); return new ViewHolderComments(v); } else throw new RuntimeException("Could not inflate layout"); } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { logger.i("onBindViewHolder, viewType: " + i); if (viewHolder instanceof ViewHolderComments) ((ViewHolderComments) viewHolder).comment.setText(comments[i].toString()); if (viewHolder instanceof ViewHolderHeader) ((ViewHolderHeader) viewHolder).header.setImageResource(R.drawable.image2); else { logger.e("no instance of viewholder found"); } } @Override public int getItemCount() { int count = comments.length + 1; logger.i("getItemCount: " + count); return count; } @Override public int getItemViewType(int position) { logger.i("getItemViewType position: " + position); if (position == HEADER) return HEADER; else return OTHER; } public static class ViewHolderComments extends RecyclerView.ViewHolder { public TextView comment; public ImageView image; public ViewHolderComments(View itemView) { super(itemView); comment = (TextView) itemView.findViewById(R.id.comment); image = (ImageView) itemView.findViewById(R.id.image); } } public static class ViewHolderHeader extends RecyclerView.ViewHolder { public final ImageView header; public ViewHolderHeader(View itemView){ super(itemView); header = (ImageView) itemView.findViewById(R.id.header_photo); } } } 

使用上面的代码,只有标题布局显示为viewType始终是0.它看起来像这样 。 如果我强制其他布局,它看起来像这样 :

有没有像listview.addHeaderView()简单的方法,但你可以通过添加一个types到你的适配器的头来实现这一点。

这是一个例子

  public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; String[] data; public HeaderAdapter(String[] data) { this.data = data; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == TYPE_ITEM) { //inflate your layout and pass it to view holder return new VHItem(null); } else if (viewType == TYPE_HEADER) { //inflate your layout and pass it to view holder return new VHHeader(null); } throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly"); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof VHItem) { String dataItem = getItem(position); //cast holder to VHItem and set data } else if (holder instanceof VHHeader) { //cast holder to VHHeader and set data for header. } } @Override public int getItemCount() { return data.length + 1; } @Override public int getItemViewType(int position) { if (isPositionHeader(position)) return TYPE_HEADER; return TYPE_ITEM; } private boolean isPositionHeader(int position) { return position == 0; } private String getItem(int position) { return data[position - 1]; } class VHItem extends RecyclerView.ViewHolder { TextView title; public VHItem(View itemView) { super(itemView); } } class VHHeader extends RecyclerView.ViewHolder { Button button; public VHHeader(View itemView) { super(itemView); } } } 

链接到要点 – >这里

简单且可重复使用的ItemDecoration

可以使用ItemDecoration轻松添加静态头文件, 而无需进行任何更改。

 // add the decoration. done. HeaderDecoration headerDecoration = new HeaderDecoration(/* init */); recyclerView.addItemDecoration(headerDecoration); 

装饰也是可重用的,因为根本不需要修改适配器或RecyclerView

下面提供的示例代码将需要一个视图添加到顶部,可以像其他任何东西一样充气。 它可以看起来像这样:

HeaderDecoration示例

为什么静态

如果您只需显示文本和图像,则此解决scheme适合您,因为它只会被绘制到列表的顶部,所以不存在用户交互(如button或查看传呼机)的可能性。

清单处理

如果没有装饰的意见,装修将不会被绘制。 你仍然需要自己处理一个空的列表。 (一种可能的解决方法是将一个虚拟项目添加到适配器。)

代码

你可以在GitHub上find完整的源代码,包括一个Builder来帮助修饰器的初始化,或者只是使用下面的代码,并将自己的值提供给构造器。

请务必为您的视图设置正确的layout_height 。 比如match_parent可能无法正常工作。

 public class HeaderDecoration extends RecyclerView.ItemDecoration { private final View mView; private final boolean mHorizontal; private final float mParallax; private final float mShadowSize; private final int mColumns; private final Paint mShadowPaint; public HeaderDecoration(View view, boolean scrollsHorizontally, float parallax, float shadowSize, int columns) { mView = view; mHorizontal = scrollsHorizontally; mParallax = parallax; mShadowSize = shadowSize; mColumns = columns; if (mShadowSize > 0) { mShadowPaint = new Paint(); mShadowPaint.setShader(mHorizontal ? new LinearGradient(mShadowSize, 0, 0, 0, new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)}, new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP) : new LinearGradient(0, mShadowSize, 0, 0, new int[]{Color.argb(55, 0, 0, 0), Color.argb(55, 0, 0, 0), Color.argb(3, 0, 0, 0)}, new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP)); } else { mShadowPaint = null; } } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); // layout basically just gets drawn on the reserved space on top of the first view mView.layout(parent.getLeft(), 0, parent.getRight(), mView.getMeasuredHeight()); for (int i = 0; i < parent.getChildCount(); i++) { View view = parent.getChildAt(i); if (parent.getChildAdapterPosition(view) == 0) { c.save(); if (mHorizontal) { c.clipRect(parent.getLeft(), parent.getTop(), view.getLeft(), parent.getBottom()); final int width = mView.getMeasuredWidth(); final float left = (view.getLeft() - width) * mParallax; c.translate(left, 0); mView.draw(c); if (mShadowSize > 0) { c.translate(view.getLeft() - left - mShadowSize, 0); c.drawRect(parent.getLeft(), parent.getTop(), mShadowSize, parent.getBottom(), mShadowPaint); } } else { c.clipRect(parent.getLeft(), parent.getTop(), parent.getRight(), view.getTop()); final int height = mView.getMeasuredHeight(); final float top = (view.getTop() - height) * mParallax; c.translate(0, top); mView.draw(c); if (mShadowSize > 0) { c.translate(0, view.getTop() - top - mShadowSize); c.drawRect(parent.getLeft(), parent.getTop(), parent.getRight(), mShadowSize, mShadowPaint); } } c.restore(); break; } } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (parent.getChildAdapterPosition(view) < mColumns) { if (mHorizontal) { if (mView.getMeasuredWidth() <= 0) { mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST)); } outRect.set(mView.getMeasuredWidth(), 0, 0, 0); } else { if (mView.getMeasuredHeight() <= 0) { mView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST)); } outRect.set(0, mView.getMeasuredHeight(), 0, 0); } } else { outRect.setEmpty(); } } } 

请注意: GitHub项目是我的个人操场。 它没有经过严格的testing,这就是为什么没有图书馆。

它有什么作用?

ItemDecoration是对列表项目的附加绘图。 在这种情况下,装饰被绘制到第一个项目的顶部。

视图被测量和布局,然后被绘制到第一项的顶部。 如果添加视差效果,它也将被剪裁到正确的边界。

随意使用我的图书馆,可在这里 。

让你为任何使用LinearLayoutManager或者GridLayoutManager RecyclerView创build一个头部View ,只需要一个简单的方法调用。

在这里输入图像说明

您可以将您的标题和您的RecyclerView放置在NestedScrollView中:

 <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <include layout="@layout/your_header"/> <android.support.v7.widget.RecyclerView android:id="@+id/list_recylclerview" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </android.support.v4.widget.NestedScrollView> 

为了使滚动正常工作,您需要禁用RecyclerView上的嵌套滚动:

 myRecyclerView.setNestedScrollingEnabled(false); 

您可以使用库SectionedRecyclerViewAdapter实现它,它具有“部分”的概念,其中部分具有页眉,页脚和内容(项目列表)。 在你的情况下,你可能只需要一个部分,但你可以有很多:

在这里输入图像说明

1)创build一个自定义的Section类:

 class MySection extends StatelessSection { List<String> myList = Arrays.asList(new String[] {"Item1", "Item2", "Item3" }); public MySection() { // call constructor with layout resources for this Section header, footer and items super(R.layout.section_header, R.layout.section_footer, R.layout.section_item); } @Override public int getContentItemsTotal() { return myList.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(myList.get(position)); } } 

2)为这些项目创build一个自定义的ViewHolder:

 class MyItemViewHolder extends RecyclerView.ViewHolder { private final TextView tvItem; public MyItemViewHolder(View itemView) { super(itemView); tvItem = (TextView) itemView.findViewById(R.id.tvItem); } } 

3)用SectionedRecyclerViewAdapter设置您的ReclyclerView

 // Create an instance of SectionedRecyclerViewAdapter SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter(); MySection mySection = new MySection(); // Add your Sections sectionAdapter.addSection(mySection); // Set up your RecyclerView with the SectionedRecyclerViewAdapter RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(sectionAdapter); 

基于这篇文章 ,我创build了一个RecyclerView.Adapter的子类,支持任意数量的页眉和页脚。

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

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

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

本地API没有这样的“addHeader”function,但具有“addItem”的概念。

我能够包括这个头的特定function,并扩展到我的FlexibleAdapter项目的页脚。 我把它叫做Scrollable Headers and Footers

在这里他们如何工作:

可滚动页眉和页脚是与所有其他页面一起滚动的特殊项目,但它们不属于主要项目(业务项目),它们总是由主要项目旁边的适配器处理。 这些项目一直位于第一和最后的位置。

在这里输入图像说明

有很多话要说,更好的阅读详细的维基页面 。

此外,FlexibleAdapter允许您创build标题/部分,也可以让他们粘性和其他数十个function,如可扩展的项目,无尽的滚动,UI扩展等…所有在一个库!

还有一个解决scheme覆盖了上面的所有用例:CompoundAdapter: https : //github.com/negusoft/CompoundAdapter-android

您可以创build一个适配器组来保存您的适配器,以及一个带有单个项目的适配器来表示标题。 代码简单易读:

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

AdapterGroup也允许嵌套,所以对于带有节的适配器,您可以为每节创build一个AdapterGroup。 然后将所有部分放在根AdapterGroup中。

去显示你在一个Recycler视图中的项目标题。 回收站视图与标题

第1步 – 添加依赖到您的gradle文件。

 compile 'com.android.support:recyclerview-v7:23.2.0' // CardView compile 'com.android.support:cardview-v7:23.2.0' 

Cardview用于装饰目的。

第2步 – 制作三个xml文件。 一个用于主要活动。第二个用于标题布局。第三个用于列表项目布局。

activity_main.xml中

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </RelativeLayout> 

header.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardElevation="2dp"> <TextView android:id="@+id/txtHeader" android:gravity="center" android:textColor="#000000" android:textSize="@dimen/abc_text_size_large_material" android:background="#DCDCDC" android:layout_width="match_parent" android:layout_height="50dp" /> </android.support.v7.widget.CardView> </LinearLayout> 

list.xml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardElevation="1dp"> <TextView android:id="@+id/txtName" android:text="abc" android:layout_width="match_parent" android:layout_height="wrap_content" /> </android.support.v7.widget.CardView> </LinearLayout> </LinearLayout> 

第3步 – 创build三个bean类。

Header.java

 public class Header extends ListItem { private String header; public Header(){} public String getHeader() { return header; } public void setHeader(String header) { this.header = header; } } 

ContentItem.java

 public class ContentItem extends ListItem { private String name; private String rollnumber; @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } public String getRollnumber() { return rollnumber; } public void setRollnumber(String rollnumber) { this.rollnumber = rollnumber; } } 

ListItem.java

 public class ListItem { private String name; public ListItem(){} public String getName() { return name; } public void setName(String name) { this.name = name; } private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } } 

第4步 – 创build一个名为MyRecyclerAdapter.java的适配器

 public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{ private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; //Header header; List<ListItem> list; public MyRecyclerAdapter(List<ListItem> headerItems) { this.list = headerItems; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(viewType==TYPE_HEADER) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false); return new VHHeader(v); } else { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false); return new VHItem(v); } // return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(holder instanceof VHHeader) { // VHHeader VHheader = (VHHeader)holder; Header currentItem = (Header) list.get(position); VHHeader VHheader = (VHHeader)holder; VHheader.txtTitle.setText(currentItem.getHeader()); } else if(holder instanceof VHItem) { ContentItem currentItem = (ContentItem) list.get(position); VHItem VHitem = (VHItem)holder; VHitem.txtName.setText(currentItem.getName()); } } public int getItemViewType(int position) { if(isPositionHeader(position)) return TYPE_HEADER; return TYPE_ITEM; } private boolean isPositionHeader(int position) { return list.get(position) instanceof Header; } @Override public int getItemCount() { return list.size(); } class VHHeader extends RecyclerView.ViewHolder{ TextView txtTitle; public VHHeader(View itemView) { super(itemView); this.txtTitle = (TextView)itemView.findViewById(R.id.txtHeader); } } class VHItem extends RecyclerView.ViewHolder{ TextView txtName; public VHItem(View itemView) { super(itemView); this.txtName = (TextView)itemView.findViewById(R.id.txtName); } } } 

步骤5 – 在MainActivity中添加以下代码:

 public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; List<List<ListItem>> arraylist; MyRecyclerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView)findViewById(R.id.my_recycler_view); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); adapter = new MyRecyclerAdapter(getList()); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setAdapter(adapter); } private ArrayList<ListItem> getList() { ArrayList<ListItem> arrayList=new ArrayList<>(); for(int j=0;j<=4;j++) { Header header=new Header(); header.setHeader("header"+j); arrayList.add(header); for (int i = 0; i <= 3; i++) { ContentItem item = new ContentItem(); item.setRollnumber(i + ""); item.setName("A" + i); arrayList.add(item); } } return arrayList; } } 

函数getList()dynamic生成头和列表项的数据。

HeaderView取决于LayoutManager。 没有一个默认的LayoutManagers支持这个,可能不会。 ListView中的HeaderView创造了很多复杂性,没有任何显着的好处。

我会build议创build一个基本适配器类,如果提供了添加标题的项目。 不要忘记重写notify *方法来根据头是否存在来正确地抵消它们。

 First - extends RecyclerView.Adapter<RecyclerView.ViewHolder> public class MenuAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 

之后 – 重写方法getItemViewTpe ***更重要

 @Override public int getItemViewType(int position) { return position; } 

方法onCreateViewHolder

 @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false); View header = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_header_item, parent, false); Log.d("onCreateViewHolder", String.valueOf(viewType)); if (viewType == 0) { return new MenuLeftHeaderViewHolder(header, onClickListener); } else { return new MenuLeftViewHolder(view, onClickListener); } } 

方法onBindViewHolder

 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position == 0) { MenuHeaderViewHolder menuHeaderViewHolder = (MenuHeaderViewHolder) holder; menuHeaderViewHolder.mTitle.setText(sMenuTitles[position]); menuHeaderViewHolder.mImage.setImageResource(sMenuImages[position]); } else { MenuViewHolder menuLeftViewHolder = (MenuLeftViewHolder) holder; menuViewHolder.mTitle.setText(sMenuTitles[position]); menuViewHolder.mImage.setImageResource(sMenuImages[position]); } } 

在完成实现ViewHolders类的静态

 public static class MenuViewHolder extends RecyclerView.ViewHolder public static class MenuLeftHeaderViewHolder extends RecyclerView.ViewHolder 

可能http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/将有所帮助。; 它只使用RecyclerView和CardView。 这是一个适配器:

 public class DifferentRowAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<CityEvent> mList; public DifferentRowAdapter(List<CityEvent> list) { this.mList = list; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case CITY_TYPE: view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_city, parent, false); return new CityViewHolder(view); case EVENT_TYPE: view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_event, parent, false); return new EventViewHolder(view); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { CityEvent object = mList.get(position); if (object != null) { switch (object.getType()) { case CITY_TYPE: ((CityViewHolder) holder).mTitle.setText(object.getName()); break; case EVENT_TYPE: ((EventViewHolder) holder).mTitle.setText(object.getName()); ((EventViewHolder) holder).mDescription.setText(object.getDescription()); break; } } } @Override public int getItemCount() { if (mList == null) return 0; return mList.size(); } @Override public int getItemViewType(int position) { if (mList != null) { CityEvent object = mList.get(position); if (object != null) { return object.getType(); } } return 0; } public static class CityViewHolder extends RecyclerView.ViewHolder { private TextView mTitle; public CityViewHolder(View itemView) { super(itemView); mTitle = (TextView) itemView.findViewById(R.id.titleTextView); } } public static class EventViewHolder extends RecyclerView.ViewHolder { private TextView mTitle; private TextView mDescription; public EventViewHolder(View itemView) { super(itemView); mTitle = (TextView) itemView.findViewById(R.id.titleTextView); mDescription = (TextView) itemView.findViewById(R.id.descriptionTextView); } } } 

这里是一个实体:

 public class CityEvent { public static final int CITY_TYPE = 0; public static final int EVENT_TYPE = 1; private String mName; private String mDescription; private int mType; public CityEvent(String name, String description, int type) { this.mName = name; this.mDescription = description; this.mType = type; } public String getName() { return mName; } public void setName(String name) { this.mName = name; } public String getDescription() { return mDescription; } public void setDescription(String description) { this.mDescription = description; } public int getType() { return mType; } public void setType(int type) { this.mType = type; } } 

这里有一些用于recyclerview的itemdecoration

 public class HeaderItemDecoration extends RecyclerView.ItemDecoration { private View customView; public HeaderItemDecoration(View view) { this.customView = view; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { super.onDraw(c, parent, state); customView.layout(parent.getLeft(), 0, parent.getRight(), customView.getMeasuredHeight()); for (int i = 0; i < parent.getChildCount(); i++) { View view = parent.getChildAt(i); if (parent.getChildAdapterPosition(view) == 0) { c.save(); final int height = customView.getMeasuredHeight(); final int top = view.getTop() - height; c.translate(0, top); customView.draw(c); c.restore(); break; } } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (parent.getChildAdapterPosition(view) == 0) { customView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST)); outRect.set(0, customView.getMeasuredHeight(), 0, 0); } else { outRect.setEmpty(); } } } 

你可以创buildaddHeaderView并使用adapter.addHeaderView(View)。

这段代码构build了多于一个头的addHeaderView。 标题应该有:android:layout_height =“wrap_content”

 public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int TYPE_ITEM = -1; public class MyViewSHolder extends RecyclerView.ViewHolder { public MyViewSHolder (View view) { super(view); } // put you code. for example: View mView; ... } public class ViewHeader extends RecyclerView.ViewHolder { public ViewHeader(View view) { super(view); } } private List<View> mHeaderViews = new ArrayList<>(); public void addHeaderView(View headerView) { mHeaderViews.add(headerView); } @Override public int getItemCount() { return ... + mHeaderViews.size(); } @Override public int getItemViewType(int position) { if (mHeaderViews.size() > position) { return position; } return TYPE_ITEM; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType != TYPE_ITEM) { //inflate your layout and pass it to view holder return new ViewHeader(mHeaderViews.get(viewType)); } ... } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int basePosition1) { if (holder instanceof ViewHeader) { return; } int basePosition = basePosition1 - mHeaderViews.size(); ... 

}

I made an implementation based on @hister's one for my personal purposes, but using inheritance.

I hide the implementation details mechanisms (like add 1 to itemCount , subtract 1 from position ) in an abstract super class HeadingableRecycleAdapter , by implementing required methods from Adapter like onBindViewHolder , getItemViewType and getItemCount , making that methods final, and providing new methods with hidden logic to client:

onAddViewHolder(RecyclerView.ViewHolder holder, int position) , onCreateViewHolder(ViewGroup parent) , itemCount()

Here are the HeadingableRecycleAdapter class and a client. I left the header layout a bit hard-coded because it fits my needs.

 public abstract class HeadingableRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int HEADER_VIEW_TYPE = 0; @LayoutRes private int headerLayoutResource; private String headerTitle; private Context context; public HeadingableRecycleAdapter(@LayoutRes int headerLayoutResourceId, String headerTitle, Context context) { this.headerLayoutResource = headerLayoutResourceId; this.headerTitle = headerTitle; this.context = context; } public Context context() { return context; } @Override public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == HEADER_VIEW_TYPE) { return new HeaderViewHolder(LayoutInflater.from(context).inflate(headerLayoutResource, parent, false)); } return onCreateViewHolder(parent); } @Override public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { int viewType = getItemViewType(position); if (viewType == HEADER_VIEW_TYPE) { HeaderViewHolder vh = (HeaderViewHolder) holder; vh.bind(headerTitle); } else { onAddViewHolder(holder, position - 1); } } @Override public final int getItemViewType(int position) { return position == 0 ? 0 : 1; } @Override public final int getItemCount() { return itemCount() + 1; } public abstract int itemCount(); public abstract RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent); public abstract void onAddViewHolder(RecyclerView.ViewHolder holder, int position); } @PerActivity public class IngredientsAdapter extends HeadingableRecycleAdapter { public static final String TITLE = "Ingredients"; private List<Ingredient> itemList; @Inject public IngredientsAdapter(Context context) { super(R.layout.layout_generic_recyclerview_cardified_header, TITLE, context); } public void setItemList(List<Ingredient> itemList) { this.itemList = itemList; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) { return new ViewHolder(LayoutInflater.from(context()).inflate(R.layout.item_ingredient, parent, false)); } @Override public void onAddViewHolder(RecyclerView.ViewHolder holder, int position) { ViewHolder vh = (ViewHolder) holder; vh.bind(itemList.get(position)); } @Override public int itemCount() { return itemList == null ? 0 : itemList.size(); } private String getQuantityFormated(double quantity, String measure) { if (quantity == (long) quantity) { return String.format(Locale.US, "%s %s", String.valueOf(quantity), measure); } else { return String.format(Locale.US, "%.1f %s", quantity, measure); } } class ViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.text_ingredient) TextView txtIngredient; ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } void bind(Ingredient ingredient) { String ingredientText = ingredient.getIngredient(); txtIngredient.setText(String.format(Locale.US, "%s %s ", getQuantityFormated(ingredient.getQuantity(), ingredient.getMeasure()), Character.toUpperCase(ingredientText.charAt(0)) + ingredientText .substring(1))); } } }