Android 5.0 – Hinzufügen von Kopf- / Fußzeilen zu einem RecyclerView

Ich verbrachte einen Moment damit, einen Weg zu finden, wie ich einen RecyclerView ohne Erfolg hinzufügen konnte. Das habe ich bis jetzt erreicht:

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

Der LayoutManager scheint das Objekt zu sein, das die Disposition der RecyclerView Elemente behandelt. Da ich keine Methode addHeaderView(View view) , entschied ich mich, mit der addView(View view, int position) -Methode des LayoutManager und meine Header-Ansicht an erster Stelle addView(View view, int position) , um wie eine Kopfzeile zu fungieren.

Und hier werden die Dinge hässlicher:

 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) 

Nachdem einige NullPointerExceptions versucht haben, addView(View view) zu verschiedenen Zeitpunkten addView(View view) der Activity-Erstellung addView(View view) nach der Einrichtung hinzugefügt wurde, selbst die Daten des Adapters), wurde mir klar, dass ich nicht weiß, ob dies der richtige Weg ist um es zu tun (und es sieht nicht so aus).

PS: Auch eine Lösung, die neben dem LinearLayoutManager den LinearLayoutManager wäre sehr zu LinearLayoutManager !

   

Ich habe einen sehr guten Artikel zu diesem https://plus.google.com/+WillBlaschko/posts/3MFmgPbQuWx gefunden

Ich musste eine Fußzeile zu meinem RecyclerView hinzufügen und hier RecyclerView ich mein Code-Snippet, da ich dachte, es könnte nützlich sein.

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

Ich hatte das gleiche Problem auf Lollipop und erstellt zwei Ansätze, um den Recyclerview Adapter zu wickeln. Man ist ziemlich einfach zu bedienen, aber ich bin nicht sicher, wie es sich mit einem sich ändernden Datensatz verhalten wird. Weil es Ihren Adapter notifyDataSetChanged und Sie müssen sicherstellen, dass Methoden wie notifyDataSetChanged auf dem richtigen notifyDataSetChanged aufgerufen werden.

Der andere sollte solche Probleme nicht haben. Lassen Sie einfach Ihren regulären Adapter die class erweitern, implementieren Sie die abstrakten Methoden und Sie sollten bereit sein. Und hier sind sie:

besteht

  • HeaderRecyclerViewAdapterV1.java Verwendung new HeaderRecyclerViewAdapterV1(new RegularAdapter());
  • Verwendung von RegularAdapter extends HeaderRecyclerViewAdapterV2

HeaderRecyclerViewAdapterV1

 import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; /** * Created by sebnapi on 08.11.14. * 

* This is a Plug-and-Play Approach for adding a Header or Footer to * a RecyclerView backed list *

* Just wrap your regular adapter like this *

* new HeaderRecyclerViewAdapterV1(new RegularAdapter()) *

* Let RegularAdapter implement HeaderRecyclerView, FooterRecyclerView or both * and you are ready to go. *

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

* With the other approach you need to let your Adapter extend HeaderRecyclerViewAdapterV2 * (and therefore change potentially more code) but possible omit these shortcomings. *

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

* If you extend this Adapter you are able to add a Header, a Footer or both * by a similar ViewHolder pattern as in RecyclerView. *

* If you want to omit changes to your class hierarchy you can try the Plug-and-Play * approach HeaderRecyclerViewAdapterV1. *

* Don't override (Be careful while overriding) * - onCreateViewHolder * - onBindViewHolder * - getItemCount * - getItemViewType *

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

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

Feedback und Gabeln geschätzt. Ich werde HeaderRecyclerViewAdapterV2 von mir selbst verwenden und die Änderungen in der Zukunft weiterentwickeln, testen und veröffentlichen.

EDIT : @OvidiuLatcu Ja, ich hatte ein paar Probleme. Tatsächlich habe ich aufgehört, den Header implizit durch position - (useHeader() ? 1 : 0) int offsetPosition(int position) und stattdessen eine public-Methode int offsetPosition(int position) dafür erstellt. Wenn Sie in Recyclerview einen OnItemTouchListener , können Sie die Berührung abfangen, die OnItemTouchListener und Y-Koordinaten der Berührung abfragen, die entsprechende untergeordnete Ansicht finden und dann recyclerView.getChildPosition(...) aufrufen, und Sie erhalten immer den nicht versetzten Wert Position im Adapter! Dies ist eine kurze Zusammenfassung im RecyclerView Code, ich sehe keine einfache Methode, dies zu überwinden. Deshalb versetze ich jetzt die Positionen explizit, wenn es nach meinem eigenen Code geht.

Sehr einfach zu lösen !!

Ich mag keine Idee, Logik im Adapter als einen anderen Ansichtstyp zu haben, weil jedes Mal nach dem Ansichtstyp gesucht wird, bevor die Ansicht zurückgegeben wird. Die folgende Lösung vermeidet zusätzliche Prüfungen.

Fügen Sie einfach LinearLayout (vertikal) Header-Ansicht + Recyclerview + Fußzeile in android.support.v4.widget.NestedScrollView hinzu .

Sieh dir das an:

         

Fügen Sie diese Codezeile für reibungsloses Scrollen hinzu

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

PS Um den Recyclingvorteil von recycleview beizubehalten, sollten Sie die Höhe wrap_content beibehalten. Siehe meinen Twitter-Thread!

Ich habe das nicht versucht, aber ich würde einfach 1 (oder 2, wenn Sie sowohl eine Kopf- und Fußzeile wollen) zu der von getItemCount in Ihrem Adapter zurückgegebenen Ganzzahl hinzufügen. Sie können dann getItemViewType in Ihrem Adapter überschreiben, um eine andere Ganzzahl zurückzugeben, wenn i==0 : https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)

createViewHolder wird dann an die Ganzzahl getItemViewType , die Sie von getItemViewType , sodass Sie den Ansichtshalter für die Kopfzeilenansicht anders erstellen oder konfigurieren können: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter. html # createViewHolder (android.view.ViewGroup , int)

Vergessen Sie nicht, eins von der Position zu subtrahieren, die an bindViewHolder .

Sie können diese GitHub- Bibliothek verwenden, um auf einfachste Weise Header und / oder Footer in Ihrem RecyclerView hinzuzufügen.

Sie müssen die HFRecyclerView- Bibliothek in Ihrem Projekt hinzufügen oder Sie können sie auch von Gradle herunterladen :

 compile 'com.mikhaellopez:hfrecyclerview:1.0.0' 

Dies ist ein Ergebnis im Bild:

Vorschau

BEARBEITEN:

Wenn Sie nur einen Rand oben und / oder unten mit dieser Bibliothek hinzufügen möchten : SimpleItemDecoration :

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

Ich habe schließlich meinen eigenen Adapter implementiert, um jeden anderen Adapter einzubinden, und Methoden zum Hinzufügen von Kopf- und Fußzeilenansichten bereitgestellt.

Erstellt hier einen Header: HeaderViewRecyclerAdapter.java

Das Hauptmerkmal, das ich wollte, war eine ähnliche Schnittstelle zu einem ListView, also wollte ich in der Lage sein, die Ansichten in meinem Fragment aufzublasen und sie dem RecyclerView in onCreateView . Dazu erstellen Sie einen HeaderViewRecyclerAdapter übergeben den Adapter, der umbrochen werden soll, und rufen addHeaderView und addFooterView um Ihre überhöhten Sichten zu übergeben. Setzen Sie dann die HeaderViewRecyclerAdapter Instanz als den Adapter auf der RecyclerView .

Eine zusätzliche Anforderung bestand darin, dass ich Adapter problemlos austauschen und gleichzeitig die Kopf- und Fußzeilen beibehalten konnte. Ich wollte nicht mehrere Adapter mit mehreren Instanzen dieser Kopf- und Fußzeilen haben. Sie können also setAdapter , um den setAdapter Adapter zu ändern und die Kopf- und Fußzeilen intakt zu lassen, wobei RecyclerView über die Änderung informiert wird.

Basierend auf @ Sebs Lösung, habe ich eine Unterklasse von RecyclerView.Adapter erstellt, die eine beliebige Anzahl von Kopf- und Fußzeilen unterstützt.

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

Obwohl es eine Lösung zu sein scheint, denke ich auch, dass dieses Ding vom LayoutManager verwaltet werden sollte. Leider brauche ich es jetzt und ich habe keine Zeit, einen StaggeredGridLayoutManager von Grund auf neu zu implementieren (noch davon zu erweitern).

Ich teste es immer noch, aber du kannst es ausprobieren, wenn du willst. Bitte lassen Sie mich wissen, wenn Sie irgendwelche Probleme damit finden.

Sie können viewtype verwenden, um dieses Problem zu lösen, hier ist meine Demo: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView

  1. Sie können einen Recycler-Anzeigemodus definieren:

    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. Überschreibe das 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. Überschreibe die getItemCount-Methode

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

4.Überschreibe die onCreateViewHolder-Methode. Erstellen Sie View Holder von 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.Überschreibe die onBindViewHolder-Methode. Binde Daten von 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()); } } } 

Ich weiß, dass ich zu spät komme, aber erst vor kurzem konnte ich einen solchen “addHeader” zum Adapter implementieren. In meinem FlexibleAdapter- Projekt können Sie setHeader auf einem Sectional- Element aufrufen und dann showAllHeaders . Wenn Sie nur einen Header benötigen, sollte der erste Eintrag den Header haben. Wenn Sie diesen Eintrag löschen, wird der Header automatisch mit dem nächsten verknüpft.

Leider sind Fußzeilen (noch) nicht abgedeckt.

Mit dem FlexibleAdapter können Sie viel mehr als nur Kopfzeilen / Abschnitte erstellen. Sie sollten wirklich einen Blick darauf casting: https://github.com/davideas/FlexibleAdapter .

Sie können die Bibliothek SectionedRecyclerViewAdapter verwenden , um Ihre Elemente in Abschnitten zu gruppieren und jedem Abschnitt eine Überschrift hinzuzufügen, wie in der folgenden Abbildung:

Bildbeschreibung hier eingeben

Zuerst erstellen Sie Ihre Sektionsklasse:

 class MySection extends StatelessSection { String title; List list; public MySection(String title, List 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); } } 

Dann richten Sie den RecyclerView mit Ihren Abschnitten ein und ändern die SpanSize der Header mit einem 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); 

Ich würde nur eine Alternative zu all diesen HeaderRecyclerViewAdapter-Implementierung hinzufügen. VerbundAdapter:

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

Dies ist ein flexiblerer Ansatz, da Sie eine Adaptergruppe aus Adaptern erstellen können. Verwenden Sie für das Headerbeispiel Ihren Adapter so wie er ist, zusammen mit einem Adapter, der ein Element für den Header enthält:

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

Es ist ziemlich einfach und lesbar. Sie können komplexere Adapter einfach nach dem gleichen Prinzip implementieren.