usar tutorial support studio recyclerview make how create como card and java android android-recyclerview android-5.0-lollipop

java - tutorial - Android 5.0: agregar encabezado/pie de página a un RecyclerView



recyclerview android 3 (12)

Pasé un momento tratando de encontrar una manera de agregar un encabezado a un RecyclerView , sin éxito.

Esto es lo que obtuve hasta ahora:

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

El LayoutManager parece ser el objeto que maneja la disposición de los elementos de RecyclerView . Como no pude encontrar ningún addHeaderView(View view) , decidí LayoutManager el LayoutManager addView(View view, int position) del LayoutManager y agregar mi vista de encabezado en la primera posición para actuar como un encabezado.

Y aquí es donde las cosas se ponen más feas:

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)

Después de obtener varias NullPointerExceptions tratando de llamar a addView(View view) en diferentes momentos de la creación de Actividad (también intenté agregar la vista una vez que todo está configurado, incluso los datos del Adaptador), me di cuenta de que no tengo idea si esta es la forma correcta para hacerlo (y no parece ser).

PD: ¡También sería muy apreciada una solución que pudiera manejar el GridLayoutManager además del LinearLayoutManager !


Simplemente agregaría una alternativa a todas esas implementaciones de HeaderRecyclerViewAdapter. Adaptador compuesto:

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

Es un enfoque más flexible, ya que puede crear un grupo de adaptadores a partir de adaptadores. Para el ejemplo de encabezado, use su adaptador tal como está, junto con un adaptador que contenga un elemento para el encabezado:

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

Es bastante simple y legible. Puede implementar un adaptador más complejo fácilmente usando el mismo principio.


mi manera de "mantenerlo simple y estúpido" ... desperdicia algunos recursos, lo sé, pero no me importa ya que mi código es simple, así que ... 1) agregue pie de página con visibilidad GONE a su item_layout

<LinearLayout android:id="@+id/footer" android:layout_width="match_parent" android:layout_height="80dp" android:orientation="vertical" android:visibility="gone"> </LinearLayout>

2) luego configúrelo visible en el último elemento

public void onBindViewHolder(ChannelAdapter.MyViewHolder holder, int position) { boolean last = position==data.size()-1; //.... holder.footer.setVisibility(View.GONE); if (last && showFooter){ holder.footer.setVisibility(View.VISIBLE); } }

hacer lo contrario para el encabezado


Basado en la solución de @ seb, creé una subclase de RecyclerView.Adapter que admite un número arbitrario 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 LayoutManager. Desafortunadamente, lo necesito ahora y no tengo tiempo para implementar un StaggeredGridLayoutManager desde cero (ni siquiera extenderlo).

Todavía lo estoy probando, pero puedes probarlo si quieres. Avíseme si encuentra algún problema con él.


Muy simple de resolver !!

No me gusta la idea de tener la lógica dentro del adaptador como un tipo de vista diferente porque cada vez que verifica el tipo de vista antes de devolver la vista. La solución a continuación evita controles adicionales.

Simplemente agregue la vista de encabezado LinearLayout (vertical) + vista de reciclaje + vista de pie de página dentro de android.support.v4.widget.NestedScrollView .

Mira esto:

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

Agregue esta línea de código para un desplazamiento suave

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

Esto perderá todo el rendimiento de RV y RV intentará diseñar todos los titulares de la vista, independientemente de la layout_height de layout_height de RV

Se recomienda su uso para la lista de tamaño pequeño como el cajón de navegación o la configuración, etc.


No he intentado esto, pero simplemente agregaría 1 (o 2, si desea un encabezado y un pie de página) al entero devuelto por getItemCount en su adaptador. Luego puede anular getItemViewType en su adaptador para devolver un número entero diferente cuando i==0 : https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)

createViewHolder continuación, createViewHolder pasa el número entero que devolvió de getItemViewType , lo que le permite crear o configurar el titular de la vista de manera diferente para la vista del encabezado: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#createViewHolder(android.view.ViewGroup , int)

No olvide restar uno del entero de posición pasado a bindViewHolder .


Puede usar esta biblioteca GitHub que permite agregar Encabezado y / o Pie de página en su RecyclerView de la manera más simple posible.

HFRecyclerView agregar la biblioteca HFRecyclerView en su proyecto o también puede obtenerla de Gradle:

compile ''com.mikhaellopez:hfrecyclerview:1.0.0''

Este es un resultado en la imagen:

EDITAR:

Si solo desea agregar un margen en la parte superior o inferior con esta biblioteca: SimpleItemDecoration :

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


Puede usar la biblioteca SectionedRecyclerViewAdapter para agrupar sus elementos en secciones y agregar un encabezado a cada sección, como en la imagen a continuación:

Primero creas tu clase de sección:

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

Luego configura RecyclerView con sus secciones y cambia el SpanSize de los encabezados con un GridLayoutManager:

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


Puede usar viewtype para resolver este problema, aquí está mi demo: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView

  1. puede definir algunos modos de visualización de la vista del reciclador:

    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.sobre el getItemViewType mothod

@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.sobre el método getItemCount

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

4.sobre el método onCreateViewHolder. crear titular de vista por viewType

@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. Anule el método onBindViewHolder. enlazar datos por 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()); } } }


Sé que llego tarde, pero solo recientemente pude implementar tal "addHeader" en el Adaptador. En mi proyecto FlexibleAdapter puede llamar a setHeader en un elemento Seccionable , luego llamar a showAllHeaders . Si solo necesita 1 encabezado, el primer elemento debe tener el encabezado. Si elimina este elemento, el encabezado se vincula automáticamente con el siguiente.

Lamentablemente, los pies de página no están cubiertos (todavía).

El Adaptador flexible le permite hacer mucho más que crear encabezados / secciones. Realmente deberías echar un vistazo: https://github.com/davideas/FlexibleAdapter .


Terminé implementando mi propio adaptador para envolver cualquier otro adaptador y proporcionar métodos para agregar vistas de encabezado y pie de página.

Creé una esencia aquí: HeaderViewRecyclerAdapter.java

La característica principal que quería era una interfaz similar a ListView, por lo que quería poder inflar las vistas en mi Fragment y agregarlas a RecyclerView en onCreateView . Esto se hace creando un HeaderViewRecyclerAdapter pasa el adaptador que se va a envolver, y llama a addHeaderView y addFooterView pasando sus vistas infladas. Luego establezca la instancia HeaderViewRecyclerAdapter como el adaptador en RecyclerView .

Un requisito adicional era que necesitaba poder cambiar fácilmente los adaptadores manteniendo los encabezados y pies de página, no quería tener múltiples adaptadores con múltiples instancias de estos encabezados y pies de página. Por lo tanto, puede llamar a setAdapter para cambiar el adaptador envuelto dejando los encabezados y pies de página intactos, y se notificará el cambio a RecyclerView .


Tuve el mismo problema en Lollipop y creé dos enfoques para envolver el adaptador Recyclerview . Uno es bastante fácil de usar, pero no estoy seguro de cómo se comportará con un conjunto de datos cambiante. Debido a que envuelve su adaptador y necesita asegurarse de llamar a métodos como notifyDataSetChanged en el objeto adaptador adecuado.

El otro no debería tener tales problemas. Simplemente deje que su adaptador regular extienda la clase, implemente los métodos abstractos y ya debería estar listo. Y aquí están:

lo esencial

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

Comentarios y tenedores apreciados. HeaderRecyclerViewAdapterV2 por mí mismo y evolucionaré, HeaderRecyclerViewAdapterV2 y publicaré los cambios en el futuro.

EDITAR : @OvidiuLatcu Sí, tuve algunos problemas. En realidad, dejé de compensar el encabezado implícitamente por position - (useHeader() ? 1 : 0) y en su lugar creé un método público int offsetPosition(int position) para él. Porque si configura un OnItemTouchListener en Recyclerview, puede interceptar el toque, obtener las coordenadas x, y del toque, encontrar la vista secundaria correspondiente y luego llamar a recyclerView.getChildPosition(...) y siempre obtendrá el no compensado posición en el adaptador! Esta es una deficiencia en el Código RecyclerView, no veo un método fácil para superar esto. Es por eso que ahora compenso las posiciones explícitas cuando lo necesito con mi propio código.


Tuve que agregar un pie de página a mi RecyclerView y aquí estoy compartiendo mi fragmento de código, ya que pensé que podría ser útil. Verifique los comentarios dentro del código para comprender mejor el flujo general.

import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; public class RecyclerViewWithFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private static final int FOOTER_VIEW = 1; private ArrayList<String> data; // Take any list that matches your requirement. private Context context; // Define a constructor public RecyclerViewWithFooterAdapter(Context context, ArrayList<String> data) { this.context = context; this.data = data; } // Define a ViewHolder 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(context).inflate(R.layout.list_item_footer, parent, false); FooterViewHolder vh = new FooterViewHolder(v); return vh; } v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false); NormalViewHolder vh = new NormalViewHolder(v); return vh; } // Now bind the ViewHolder 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 } } }

El fragmento de código anterior agrega un pie de página a RecyclerView . Puede consultar este repositorio de GitHub para verificar la implementación de agregar tanto el encabezado como el pie de página.