¿Hay un equivalente addHeaderView para RecyclerView?

Estoy buscando un equivalente a addHeaderView para una vista de reciclador. Básicamente quiero que se agregue una imagen con 2 botones como encabezado a la vista de lista. ¿Hay alguna manera diferente de agregar una vista de encabezado a una vista de reciclador? Un ejemplo de guía sería útil

EDIT 2 (diseños de fragmentos añadidos):

Después de agregar instrucciones de registro, parece que getViewType solo recibe una posición de 0. Esto lleva a que onCreateView solo cargue el diseño único:

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 

La transición de fragmento para cargar 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(); } } 

The Fragment’s 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; } 

El fragmento que contiene la vista de reciclaje:

      

El diseño de fila de comentarios:

          

El encabezado

    

El código del Adaptador (gracias a hister para ayudarme a comenzar):

 public class CommentAdapter extends RecyclerView.Adapter{ 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); } } } 

Con el código anterior, solo se muestra el diseño del encabezado, ya que viewType es siempre 0. Se ve así. Si forzo el otro diseño, se ve así:

No hay una manera fácil como listview.addHeaderView() pero puede lograr esto agregando un tipo a su adaptador para el encabezado.

Aquí hay un ejemplo

 public class HeaderAdapter extends RecyclerView.Adapter { 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); } } } 

link to gist -> aquí

ItemDecoration fácil y reutilizable

Los encabezados estáticos se pueden agregar fácilmente con una ItemDecoration y sin ningún otro cambio.

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

La decoración también es reutilizable ya que no hay necesidad de modificar el adaptador o el RecyclerView en absoluto.

El código de muestra proporcionado a continuación requerirá una vista para agregar a la parte superior que puede inflarse como todo lo demás. Puede verse así:

Muestra de HeaderDecoration

Por qué estático ?

Si solo tiene que mostrar texto e imágenes, esta solución es para usted; no hay posibilidad de interacción del usuario, como botones o visualización de buscapersonas, ya que solo se dibujará en la parte superior de la lista.

Manejo de listas vacías

Si no hay vista para decorar, la decoración no se dibujará. Aún tendrá que manejar una lista vacía usted mismo. (Una posible solución alternativa sería agregar un elemento ficticio al adaptador).

El código

Puede encontrar el código fuente completo aquí en GitHub, incluido un Builder para ayudar con la inicialización del decorador, o simplemente use el código a continuación y proporcione sus propios valores al constructor.

Asegúrese de establecer un layout_height correcto para su vista. por ejemplo, match_parent podría no funcionar correctamente.

 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.restre(); 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(); } } } 

Tenga en cuenta: El proyecto GitHub es mi patio de recreo personal. No está probado exhaustivamente, por lo que todavía no hay una biblioteca.

¿Qué hace?

Un ItemDecoration es un dibujo adicional a un elemento de una lista. En este caso, se dibuja una decoración en la parte superior del primer elemento.

La vista se mide y presenta, luego se dibuja en la parte superior del primer elemento. Si se agrega un efecto de paralaje, también se recortará a los límites correctos.

Siéntase libre de usar mi biblioteca, disponible aquí .

Te permite crear una View encabezado para cualquier RecyclerView que use LinearLayoutManager o GridLayoutManager con solo una simple llamada a un método.

enter image description here

Puede colocar su encabezado y su RecyclerView en un NestedScrollView:

       

Para que el desplazamiento funcione correctamente, debe desactivar el desplazamiento nested en su RecyclerView:

 myRecyclerView.setNestedScrollingEnabled(false); 

Vamos a mostrarle que haga un encabezado con elementos en una vista de reciclador. Vista de reciclador con encabezado

Paso 1- Agregue dependencia a su archivo gradle.

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

Cardview se usa con fines decorativos.

Paso 2: crea tres archivos xml. Uno para la actividad principal. Segundo para el diseño del encabezado. Tercero para el diseño del elemento de la lista.

activity_main.xml

     

header.xml

      

list.xml

        

Paso 3- Crea tres clases de 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; } } 

Paso 4- Crea un adaptador llamado MyRecyclerAdapter.java

 public class MyRecyclerAdapter extends RecyclerView.Adapter{ private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; //Header header; List list; public MyRecyclerAdapter(List 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); } } } 

Paso 5- En MainActivity agrega el siguiente código:

 public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; List> 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 getList() { ArrayList 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; } } 

La función getList () genera dinámicamente los datos para los encabezados y para los elementos de la lista.

Puede lograrlo utilizando la biblioteca SectionedRecyclerViewAdapter , tiene el concepto de “Secciones”, donde la Sección tiene un encabezado, pie de página y contenido (lista de elementos). En su caso, es posible que solo necesite una Sección pero puede tener muchas:

enter image description here

1) Crear una clase de sección personalizada:

 class MySection extends StatelessSection { List 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) Crea un ViewHolder personalizado para los artículos:

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

3) Configure su ReclyclerView con SectionedRecyclerViewAdapter

 // 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); 

Basado en esta publicación , creé una subclase de RecyclerView.Adapter que admite una cantidad arbitraria de encabezados y pies de página.

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

Aunque parece ser una solución, también creo que esto debería ser administrado por el LayoutManager. Desafortunadamente, ahora lo necesito y no tengo tiempo para implementar un StaggeredLridLayoutManager desde cero (ni siquiera extenderlo).

Todavía estoy probándolo, pero puedes probarlo si quieres. Por favor, avíseme si encuentra algún problema con él.

La API nativa no tiene esa función de “agregar encabezado”, pero tiene el concepto de “addItem”.

Pude incluir esta característica específica de los encabezados y también para los pies de página en mi proyecto FlexibleAdapter . Lo llamé Encabezados y pies de página desplazables .

Aquí cómo funcionan:

Los encabezados y pies de página desplazables son elementos especiales que se desplazan junto con todos los demás, pero no pertenecen a los elementos principales (elementos de negocios) y siempre los maneja el adaptador junto a los elementos principales. Esos artículos se encuentran persistentemente en la primera y última posición.

enter image description here

Hay mucho que decir sobre ellos, es mejor leer la página wiki detallada.

Además, FlexibleAdapter te permite crear encabezados / secciones, también puedes tenerlos pegajosos y decenas de otras características como elementos expandibles, scroll infinito, extensiones de interfaz de usuario, etc. ¡biblioteca todo en uno!

Existe una solución más que cubre todos los casos de uso anteriores: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

Puede crear un AdapterGroup que contenga su Adaptador tal como está, junto con un adaptador con un solo elemento para representar el encabezado. El código es fácil y legible:

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

AdapterGroup también permite la anidación, por lo que para un adaptador con secciones, puede crear un AdapterGroup por sección. A continuación, coloque todas las secciones en un AdapterGroup raíz.

HeaderView depende del LayoutManager. Ninguno de los LayoutManagers predeterminados lo admite y probablemente no sea así. HeaderView en ListView crea mucha complejidad sin ningún beneficio significativo.

Sugiero crear una clase de adaptador base que agregue elementos para Encabezados si se proporcionan. No olvide sobrescribir los métodos de notificación * para compensarlos adecuadamente dependiendo de si el encabezado está presente o no.

 First - extends RecyclerView.Adapter public class MenuAdapter extends RecyclerView.Adapter { 

After – Anula el método getItemViewTpe *** Más importante

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

método 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); } } 

método 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]); } } 

en acabado implementa la clase ViewHolders estática

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

aquí algunos artículosdecoración para recyclerview

 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(); } } } 

Probablemente http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ ayudará. Utiliza solo RecyclerView y CardView. Aquí hay un adaptador:

 public class DifferentRowAdapter extends RecyclerView.Adapter { private List mList; public DifferentRowAdapter(List 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); } } } 

Y aquí hay una entidad:

 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; } } 

Hice una implementación basada en la de @ hister para mis propósitos personales, pero usando herencia.

itemCount mecanismos de detalles de implementación (como agregar 1 a itemCount , restar 1 de position ) en una HeadingableRecycleAdapter abstracta HeadingableRecycleAdapter , implementando los métodos requeridos de Adapter como onBindViewHolder , getItemViewType y getItemCount , haciendo que los métodos sean definitivos y proporcionen nuevos métodos con lógica oculta al cliente:

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

Aquí están la clase HeadingableRecycleAdapter y un cliente. Dejé el diseño del encabezado un poco codificado porque se ajusta a mis necesidades.

 public abstract class HeadingableRecycleAdapter extends RecyclerView.Adapter { 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 itemList; @Inject public IngredientsAdapter(Context context) { super(R.layout.layout_generic_recyclerview_cardified_header, TITLE, context); } public void setItemList(List 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))); } } } 

you can create addHeaderView and use

adapter.addHeaderView(View) .

This code build the addHeaderView for more then one header. the headers should have:

android:layout_height="wrap_content"

 public class MyAdapter extends RecyclerView.Adapter { 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 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(); ... } } 

Maybe wrap header and recyclerview into a coordinatorlayout :