Existe-t-il un équivalent à addHeaderView pour RecyclerView?

Je cherche un équivalent à addHeaderView pour une vue recycleur. Fondamentalement, je veux qu’une image avec 2 boutons soit ajoutée comme en-tête à la liste. Existe-t-il une autre façon d’append une vue d’en-tête à une vue de recycleur? Un exemple d’orientation serait utile

EDIT 2 (ajout de mises en page de fragment):

Après avoir ajouté des instructions de journal, il semble que getViewType ne reçoive jamais la position 0. Cela conduit à onCreateView qui ne charge que la mise en page:

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 transition de fragment pour charger le commentaireFragment:

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

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

Le fragment contenant le recycleview:

      

La disposition des lignes de commentaires:

          

L’en-tête

    

Le code de l’adaptateur (merci à hister pour me lancer):

 public class CommentAdapter extends RecyclerView.Adapter{ private final int rowCardLayout; public static Context mContext; private final int headerLayout; private final Ssortingng [] comments; private static final int HEADER = 0; private static final int OTHER = 0; public CommentAdapter(int rowCardLayout, int headerLayout, Context context, Ssortingng [] 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].toSsortingng()); 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); } } } 

En utilisant le code ci-dessus, seule la disposition en-tête est affichée car viewType est toujours 0. Il ressemble à ceci . Si je force l’autre disposition, il ressemble à ceci :

Il n’y a pas de moyen simple comme listview.addHeaderView() mais vous pouvez y parvenir en ajoutant un type à votre adaptateur pour l’en-tête.

Voici un exemple

 public class HeaderAdapter extends RecyclerView.Adapter { private static final int TYPE_HEADER = 0; private static final int TYPE_ITEM = 1; Ssortingng[] data; public HeaderAdapter(Ssortingng[] 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) { Ssortingng 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 Ssortingng 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); } } } 

lien vers gist -> ici

Facile et réutilisable ItemDecoration

Les en- têtes statiques peuvent facilement être ajoutés à un ItemDecoration et sans autres modifications.

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

La décoration est également réutilisable puisqu’il n’est pas nécessaire de modifier l’adaptateur ou le RecyclerView .

L’exemple de code fourni ci-dessous nécessitera une vue pour l’append en haut, ce qui peut simplement être gonflé comme tout le rest. Cela peut ressembler à ceci:

HeaderDecoration échantillon

Pourquoi statique ?

Si vous devez simplement afficher du texte et des images, cette solution est pour vous: il n’y a aucune possibilité d’interaction avec l’utilisateur, comme des boutons ou des pagers de vue, car elle sera simplement dessinée en haut de votre liste.

Traitement des listes vides

S’il n’y a pas de vue à décorer, la décoration ne sera pas dessinée. Vous devrez toujours gérer vous-même une liste vide. (Une solution possible serait d’append un élément factice à l’adaptateur.)

Le code

Vous pouvez trouver le code source complet ici sur GitHub, y compris un Builder pour vous aider à initialiser le décorateur, ou simplement utiliser le code ci-dessous et fournir vos propres valeurs au constructeur.

S’il vous plaît assurez-vous de définir une mise en page correcte pour votre vue. Par exemple, match_parent pourrait ne pas fonctionner correctement.

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

Veuillez noter que le projet GitHub est mon terrain de jeu personnel. Il n'est pas complètement testé, c'est pourquoi il n'y a pas encore de bibliothèque.

Qu'est ce que ça fait?

Un ItemDecoration est un dessin supplémentaire à un élément d'une liste. Dans ce cas, une décoration est dessinée en haut du premier élément.

La vue est mesurée et mise en forme, puis dessinée en haut du premier élément. Si un effet de parallaxe est ajouté, il sera également coupé aux limites correctes.

N’hésitez pas à utiliser ma bibliothèque, disponible ici .

Il vous permet de créer un en-tête View pour tout RecyclerView qui utilise LinearLayoutManager ou GridLayoutManager avec un simple appel de méthode.

entrer la description de l'image ici

Vous pouvez simplement placer votre en-tête et votre RecyclerView dans un NestedScrollView:

       

Pour que le défilement fonctionne correctement, vous devez désactiver le défilement nested sur votre RecyclerView:

 myRecyclerView.setNestedScrollingEnabled(false); 

Vous allez montrer comment créer un en-tête avec des éléments dans une vue Recycler. Vue du recycleur avec en-tête

Étape 1 – Ajouter une dépendance dans votre fichier de graduation.

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

Cardview est utilisé à des fins de décoration.

Step2- Faites trois fichiers xml. Un pour l’activité principale. Deuxième pour la disposition des en-têtes. Troisième pour la disposition des éléments de la liste.

activity_main.xml

     

header.xml

      

list.xml

        

Étape 3 – Créez trois classes de haricots.

Header.java

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

ContentItem.java

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

ListItem.java

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

Étape 4- Créez un adaptateur nommé 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); } } } 

Étape 5 – Dans MainActivity, ajoutez le code suivant:

 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 fonction getList () génère dynamicment les données pour les en-têtes et les éléments de liste.

Vous pouvez le faire en utilisant la bibliothèque SectionedRecyclerViewAdapter , il a le concept de “Sections”, où quelle section a un en-tête, un pied de page et un contenu (liste des éléments). Dans votre cas, vous n’avez peut-être besoin que d’une section, mais vous pouvez en avoir plusieurs:

entrer la description de l'image ici

1) Créez une classe de section personnalisée:

 class MySection extends StatelessSection { List myList = Arrays.asList(new Ssortingng[] {"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) Créez un ViewHolder personnalisé pour les éléments:

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

3) Configurez votre ReclyclerView avec le 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); 

Sur la base de cet article , j’ai créé une sous-classe de RecyclerView.Adapter qui prend en charge un nombre arbitraire d’en-têtes et de pieds de page.

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

Bien que cela semble être une solution, je pense également que cette chose devrait être gérée par le LayoutManager. Malheureusement, j’en ai besoin maintenant et je n’ai pas le temps d’implémenter un StaggeredGridLayoutManager à partir de rien (ni même de l’étendre).

Je le teste toujours, mais vous pouvez l’essayer si vous le souhaitez. S’il vous plaît laissez-moi savoir si vous trouvez des problèmes avec elle.

L’API native ne possède pas une telle fonctionnalité “addHeader”, mais a le concept de “addItem”.

J’ai été en mesure d’inclure cette fonctionnalité spécifique des en-têtes et de l’étendre aux pieds de page dans mon projet FlexibleAdapter . Je l’ai appelé en -têtes et pieds de page défilables .

Voici comment ils fonctionnent:

Les en-têtes et les pieds de page défilables sont des éléments spéciaux qui défilent avec tous les autres, mais ils n’appartiennent pas aux éléments principaux (éléments de gestion) et sont toujours gérés par l’adaptateur à côté des éléments principaux. Ces éléments sont situés de manière persistante aux première et dernière positions.

entrer la description de l'image ici

Il y a beaucoup à dire à leur sujet, mieux vaut lire la page wiki détaillée.

De plus, le FlexibleAdapter vous permet de créer des en-têtes / sections, vous pouvez également les intégrer à des dizaines d’autres fonctionnalités telles que des éléments extensibles, un défilement sans fin, des extensions d’interface utilisateur, etc. dans une seule bibliothèque!

Il y a une autre solution qui couvre tous les cas d’utilisation ci-dessus: CompoundAdapter: https://github.com/negusoft/CompoundAdapter-android

Vous pouvez créer un AdapterGroup qui contient votre adaptateur tel quel, ainsi qu’un adaptateur avec un seul élément pour représenter l’en-tête. Le code est facile et lisible:

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

AdapterGroup permet également l’imbrication, donc pour un adaptateur avec des sections, vous pouvez créer un AdapterGroup par section. Ensuite, placez toutes les sections dans un AdapterGroup racine.

HeaderView dépend du LayoutManager. Aucun des LayoutManagers par défaut ne le supporte et probablement pas. HeaderView dans ListView crée beaucoup de complexité sans aucun avantage significatif.

Je suggère de créer une classe d’adaptateur de base qui ajoute des éléments pour les en-têtes s’ils sont fournis. N’oubliez pas de remplacer les méthodes notify * pour les compenser correctement selon que l’en-tête est présent ou non.

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

Après – Remplacer la méthode getItemViewTpe *** Plus important

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

méthode 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", Ssortingng.valueOf(viewType)); if (viewType == 0) { return new MenuLeftHeaderViewHolder(header, onClickListener); } else { return new MenuLeftViewHolder(view, onClickListener); } } 

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

in finish implémente la classe ViewHolders statique

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

Voici quelques articlesdécoration pour 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(); } } } 

Probablement http://alexzh.com/tutorials/multiple-row-layouts-using-recyclerview/ aidera. Il utilise uniquement RecyclerView et CardView. Voici un adaptateur:

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

Et voici une entité:

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

J’ai réalisé une implémentation basée sur celle de @ hister à des fins personnelles, mais en utilisant l’inheritance.

Je cache les mécanismes de détails d’implémentation (comme add 1 à itemCount , soustrais 1 de la position ) dans une super-classe abstraite HeadingableRecycleAdapter , en implémentant les méthodes requirejses de Adapter comme onBindViewHolder , getItemViewType et getItemCount , rendant ces méthodes définitives au client:

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

Voici la classe HeadingableRecycleAdapter et un client. J’ai laissé la disposition de l’en-tête un peu codée car elle correspond à mes besoins.

 public abstract class HeadingableRecycleAdapter extends RecyclerView.Adapter { private static final int HEADER_VIEW_TYPE = 0; @LayoutRes private int headerLayoutResource; private Ssortingng headerTitle; private Context context; public HeadingableRecycleAdapter(@LayoutRes int headerLayoutResourceId, Ssortingng 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 Ssortingng 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 Ssortingng getQuantityFormated(double quantity, Ssortingng measure) { if (quantity == (long) quantity) { return Ssortingng.format(Locale.US, "%s %s", Ssortingng.valueOf(quantity), measure); } else { return Ssortingng.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) { Ssortingng ingredientText = ingredient.getIngredient(); txtIngredient.setText(Ssortingng.format(Locale.US, "%s %s ", getQuantityFormated(ingredient.getQuantity(), ingredient.getMeasure()), Character.toUpperCase(ingredientText.charAt(0)) + ingredientText .subssortingng(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 :