La hauteur de vue de Recycleur imbriqué n'emballe pas son contenu

J'ai une application qui gère des collections de livres (comme les listes de lecture).

Je souhaite afficher une liste de collection avec un RecyclerView vertical et à l'intérieur de chaque ligne, une liste de livre dans une RecyclerView horizontale.

Lorsque j'ai configuré le layout_height de la RecyclerView horizontale interne à 300dp, il s'affiche correctement, mais quand je l'ai configuré pour wrap_content, il n'affiche rien. J'ai besoin d'utiliser wrap_content parce que je veux pouvoir modifier le gestionnaire de mise en page de manière programmée pour passer d'un affichage vertical à un affichage horizontal.

Entrez la description de l'image ici

Savez-vous ce que je fais mal?

Ma disposition de fragment:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> <com.twibit.ui.view.CustomSwipeToRefreshLayout android:id="@+id/swipe_container" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/shelf_collection_listview" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingTop="10dp"/> </LinearLayout> </com.twibit.ui.view.CustomSwipeToRefreshLayout> </LinearLayout> 

Disposition des éléments de collection:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFF"> <!-- Simple Header --> </RelativeLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="@string/empty_collection" android:id="@+id/empty_collection_tv" android:visibility="gone" android:gravity="center"/> <android.support.v7.widget.RecyclerView android:id="@+id/collection_book_listview" android:layout_width="match_parent" android:layout_height="wrap_content"/> <!-- android:layout_height="300dp" --> </FrameLayout> </LinearLayout> 

Article de liste de livres:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="180dp" android:layout_height="220dp" android:layout_gravity="center"> <ImageView android:id="@+id/shelf_item_cover" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:maxWidth="150dp" android:maxHeight="200dp" android:src="@drawable/placeholder" android:contentDescription="@string/cover" android:adjustViewBounds="true" android:background="@android:drawable/dialog_holo_light_frame"/> </FrameLayout> 

Voici mon adaptateur de collection:

 private class CollectionsListAdapter extends RecyclerView.Adapter<CollectionsListAdapter.ViewHolder> { private final String TAG = CollectionsListAdapter.class.getSimpleName(); private Context mContext; // Create the ViewHolder class to keep references to your views class ViewHolder extends RecyclerView.ViewHolder { private final TextView mHeaderTitleTextView; private final TextView mHeaderCountTextView; private final RecyclerView mHorizontalListView; private final TextView mEmptyTextView; public ViewHolder(View view) { super(view); mHeaderTitleTextView = (TextView) view.findViewById(R.id.collection_header_tv); mHeaderCountTextView = (TextView) view.findViewById(R.id.collection_header_count_tv); mHorizontalListView = (RecyclerView) view.findViewById(R.id.collection_book_listview); mEmptyTextView = (TextView) view.findViewById(R.id.empty_collection_tv); } } public CollectionsListAdapter(Context context) { mContext = context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int i) { Log.d(TAG, "CollectionsListAdapter.onCreateViewHolder(" + parent.getId() + ", " + i + ")"); // Create a new view by inflating the row item xml. View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.shelf_collection, parent, false); // Set the view to the ViewHolder ViewHolder holder = new ViewHolder(v); holder.mHorizontalListView.setHasFixedSize(false); holder.mHorizontalListView.setHorizontalScrollBarEnabled(true); // use a linear layout manager LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext); mLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); holder.mHorizontalListView.setLayoutManager(mLayoutManager); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int i) { Log.d(TAG, "CollectionsListAdapter.onBindViewHolder(" + holder.getPosition() + ", " + i + ")"); Collection collection = mCollectionList.get(i); Log.d(TAG, "Collection : " + collection.getLabel()); holder.mHeaderTitleTextView.setText(collection.getLabel()); holder.mHeaderCountTextView.setText("" + collection.getBooks().size()); // Create an adapter if none exists if (!mBookListAdapterMap.containsKey(collection.getCollectionId())) { mBookListAdapterMap.put(collection.getCollectionId(), new BookListAdapter(getActivity(), collection)); } holder.mHorizontalListView.setAdapter(mBookListAdapterMap.get(collection.getCollectionId())); } @Override public int getItemCount() { return mCollectionList.size(); } } 

Et enfin, l'adaptateur Book:

 private class BookListAdapter extends RecyclerView.Adapter<BookListAdapter.ViewHolder> implements View.OnClickListener { private final String TAG = BookListAdapter.class.getSimpleName(); // Create the ViewHolder class to keep references to your views class ViewHolder extends RecyclerView.ViewHolder { public ImageView mCoverImageView; public ViewHolder(View view) { super(view); mCoverImageView = (ImageView) view.findViewById(R.id.shelf_item_cover); } } @Override public void onClick(View v) { BookListAdapter.ViewHolder holder = (BookListAdapter.ViewHolder) v.getTag(); int position = holder.getPosition(); final Book book = mCollection.getBooks().get(position); // Click on cover image if (v.getId() == holder.mCoverImageView.getId()) { downloadOrOpenBook(book); return; } } private void downloadOrOpenBook(final Book book) { // do stuff } private Context mContext; private Collection mCollection; public BookListAdapter(Context context, Collection collection) { Log.d(TAG, "BookListAdapter(" + context + ", " + collection + ")"); mCollection = collection; mContext = context; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int i) { Log.d(TAG, "onCreateViewHolder(" + parent.getId() + ", " + i + ")"); // Create a new view by inflating the row item xml. View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.shelf_grid_item, parent, false); // Set the view to the ViewHolder ViewHolder holder = new ViewHolder(v); holder.mCoverImageView.setOnClickListener(BookListAdapter.this); // Download or Open holder.mCoverImageView.setTag(holder); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int i) { Log.d(TAG, "onBindViewHolder(" + holder.getPosition() + ", " + i + ")"); Book book = mCollection.getBooks().get(i); ImageView imageView = holder.mCoverImageView; ImageLoader.getInstance().displayImage(book.getCoverUrl(), imageView); } @Override public int getItemCount() { return mCollection.getBooks().size(); } } 

  • Comment cacher la barre d'outils dans un ViewPager lors de l'utilisation de Scrolling RecyclerView?
  • Comment obtenir un contexte dans un adaptateur de vue de recyclage
  • RecyclerView Endless Infinite Scrolling Issue
  • Android Horizontal Recycler Voir scroll Direction
  • Insérer des annonces dans Recyclerview
  • Désactiver cliquez sur RecyclerView à l'intérieur d'un SwipeRefreshLayout
  • Manipuler l'article en cliquant longuement sur la vue du recycleur
  • NotifyDataSetChanged ne fonctionne pas sur RecyclerView
  • 14 Solutions collect form web for “La hauteur de vue de Recycleur imbriqué n'emballe pas son contenu”

    Mettre à jour

    Beaucoup de problèmes liés à cette fonctionnalité dans la version 23.2.0 ont été corrigés en 23.2.1, à la place de cela.

    Avec la version de Support Library version 23.2, RecyclerView prend désormais en charge cela!

    Mettre à jour build.gradle vers:

     compile 'com.android.support:recyclerview-v7:23.2.1' 

    Ou toute autre version au-delà.

    Cette version apporte une nouvelle fonctionnalité intéressante à l'API LayoutManager: mesure automatique! Cela permet à un RecyclerView de se tailler en fonction de la taille de son contenu. Cela signifie que les scénarios précédemment indisponibles, tels que l'utilisation de WRAP_CONTENT pour une dimension de RecyclerView, sont maintenant possibles. Vous trouverez tout ce qui est construit dans LayoutManagers prend désormais en charge la mesure automatique.

    Cela peut être désactivé via setAutoMeasurementEnabled() si nécessaire. Vérifiez en détail ici .

    @ User2302510 solution fonctionne pas aussi bien que vous le pouvez. La solution de rechange complète pour les deux orientations et les changements dynamiques de données est:

     public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

    Le code ci-dessus ne fonctionne pas bien lorsque vous devez créer vos éléments "wrap_content", car il mesure les deux hauteur et largeur des éléments avec MeasureSpec.UNSPECIFIED. Après quelques problèmes, j'ai modifié cette solution afin que les éléments puissent se développer. La seule différence est qu'il fournit aux parents une hauteur ou une largeur. MeasureSpec dépend de l'orientation de la disposition.

     public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { if (getOrientation() == HORIZONTAL) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), heightSpec, mMeasuredDimension); width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { measureScrapChild(recycler, i, widthSpec, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); recycler.bindViewToPosition(view, position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

    Le gestionnaire de mise en page existant ne supporte pas encore le contenu de l'emballage.

    Vous pouvez créer un nouveau LayoutManager qui étend le système existant et surmonte la méthode Measure pour mesurer le contenu de l'emballage.

    Comme l'a mentionné @ yiğit, vous devez annuler onMeasure (). Les deux @ user2302510 et @DenisNek ont ​​de bonnes réponses, mais si vous souhaitez prendre en charge ItemDecoration, vous pouvez utiliser ce gestionnaire de mise en page personnalisé.

    Et les autres réponses ne peuvent pas faire défiler quand il y a plus d'éléments que l'on peut afficher sur l'écran. Celui-ci utilise la mise en œuvre par défaut de onMeasure () lorsqu'il y a plus d'éléments que la taille de l'écran.

     public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } // If child view is more than screen size, there is no need to make it wrap content. We can use original onMeasure() so we can scroll view. if (height < heightSize && width < widthSize) { switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } else { super.onMeasure(recycler, state, widthSpec, heightSpec); } } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); // For adding Item Decor Insets to view super.measureChildWithMargins(view, 0, 0); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom() + getPaddingBottom() + getDecoratedBottom(view) , p.height); view.measure(childWidthSpec, childHeightSpec); // Get decorated measurements measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin; measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

    Et si vous souhaitez l'utiliser avec GridLayoutManager, il suffit de l'étendre à GridLayoutManager et de changer

     for (int i = 0; i < getItemCount(); i++) 

    à

     for (int i = 0; i < getItemCount(); i = i + getSpanCount()) 

    MISE À JOUR Mars 2016

    Par Android Support Library 23.2.1 d'une version de la bibliothèque de support. Donc tout WRAP_CONTENT devrait fonctionner correctement.

    Veuillez mettre à jour la version d'une bibliothèque dans un fichier gradle.

     compile 'com.android.support:recyclerview-v7:23.2.1' 

    Cela permet à un RecyclerView de se tailler en fonction de la taille de son contenu. Cela signifie que les scénarios précédemment indisponibles, tels que l' utilisation de WRAP_CONTENT pour une dimension de RecyclerView, sont maintenant possibles .

    Vous devrez appeler setAutoMeasureEnabled (true)

    Correction de bugs liés à différentes méthodes de mesure-spécification dans la mise à jour

    Cochez la case https://developer.android.com/topic/libraries/support-library/features.html

    Cette réponse est basée sur la solution donnée par Denis Nek. Il résout le problème de ne pas prendre en compte les décorations comme les diviseurs.

     public class WrappingRecyclerViewLayoutManager extends LinearLayoutManager { public WrappingRecyclerViewLayoutManager(Context context) { super(context, VERTICAL, false); } public WrappingRecyclerViewLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); Rect outRect = new Rect(); calculateItemDecorationsForChild(view, outRect); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin + outRect.bottom + outRect.top; recycler.recycleView(view); } } 

    }

    Une alternative pour étendre LayoutManager peut être simplement définir la taille de la vue manuellement.

    Nombre d'articles par hauteur de rangée (si tous les éléments ont la même hauteur et que le séparateur est inclus sur la rangée)

     LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) mListView.getLayoutParams(); params.height = mAdapter.getItemCount() * getResources().getDimensionPixelSize(R.dimen.row_height); mListView.setLayoutParams(params); 

    Est toujours une solution de contournement, mais pour les cas de base, cela fonctionne.

    Solution utilisée à partir de @ sinan-kozak, sauf réparé quelques bogues. Plus précisément, nous ne devrions pas utiliser View.MeasureSpec.UNSPECIFIED pour la largeur et la hauteur lorsque vous appelez measureScrapChild car cela ne correspond pas correctement au texte enroulé dans l'enfant. Au lieu de cela, nous passerons les modes largeur et hauteur du parent, ce qui permettra aux choses de fonctionner pour les mises en page horizontales et verticales.

     public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { if (getOrientation() == HORIZONTAL) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(heightSize, heightMode), mMeasuredDimension); width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(widthSize, widthMode), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } // If child view is more than screen size, there is no need to make it wrap content. We can use original onMeasure() so we can scroll view. if (height < heightSize && width < widthSize) { switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } else { super.onMeasure(recycler, state, widthSpec, heightSpec); } } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); // For adding Item Decor Insets to view super.measureChildWithMargins(view, 0, 0); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view) , p.height); view.measure(childWidthSpec, childHeightSpec); // Get decorated measurements measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin; measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

    `

    Je trouve ici une solution: https://code.google.com/p/android/issues/detail?id=74772

    Ce n'est en aucun cas ma solution. Je viens de le copier à partir de là, mais j'espère que cela aidera quelqu'un autant qu'il m'a aidé lors de la mise en œuvre de la RecyclerView horizontale et de la hauteur Wrap_content (devrait également fonctionner pour la largeur verticale et la longueur de wrap_content)

    La solution consiste à étendre le LayoutManager et à remplacer sa méthode onMeasure comme l'a suggéré @yigit.

    Voici le code au cas où le lien décéderait:

     public static class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context) { super(context); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); measureScrapChild(recycler, 0, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); int width = mMeasuredDimension[0]; int height = mMeasuredDimension[1]; switch (widthMode) { case View.MeasureSpec.EXACTLY: case View.MeasureSpec.AT_MOST: width = widthSize; break; case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: case View.MeasureSpec.AT_MOST: height = heightSize; break; case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth(); measuredDimension[1] = view.getMeasuredHeight(); recycler.recycleView(view); } } } 

    La bibliothèque de support Android prend désormais en charge la propriété WRAP_CONTENT. Importez-le juste dans votre diplôme.

     compile 'com.android.support:recyclerview-v7:23.2.0' 

    Et fait!

    Sur la base du travail de Denis Nek, cela fonctionne bien si la somme des largeurs de l'objet est inférieure à la taille du conteneur. Autre que cela, il rendra le recyclage non scrollable et ne montrera que le sous-ensemble des données.

    to solve this problem, i modified the solution alittle so that it choose the min of the provided size and calculated size. see below:

     package com.linkdev.gafi.adapters; import android.content.Context; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import com.linkdev.gafi.R; public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); this.c = context; } private Context c; private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); if (getOrientation() == HORIZONTAL) { width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } int widthDesired = Math.min(widthSize,width); setMeasuredDimension(widthDesired, height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } }} 

    I have tried all solutions, they are very useful but this only works fine for me

     public class LinearLayoutManager extends android.support.v7.widget.LinearLayoutManager { public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } private int[] mMeasuredDimension = new int[2]; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; for (int i = 0; i < getItemCount(); i++) { if (getOrientation() == HORIZONTAL) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), heightSpec, mMeasuredDimension); width = width + mMeasuredDimension[0]; if (i == 0) { height = mMeasuredDimension[1]; } } else { measureScrapChild(recycler, i, widthSpec, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), mMeasuredDimension); height = height + mMeasuredDimension[1]; if (i == 0) { width = mMeasuredDimension[0]; } } } if (height < heightSize || width < widthSize) { switch (widthMode) { case View.MeasureSpec.EXACTLY: width = widthSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } switch (heightMode) { case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.AT_MOST: case View.MeasureSpec.UNSPECIFIED: } setMeasuredDimension(width, height); } else { super.onMeasure(recycler, state, widthSpec, heightSpec); } } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { View view = recycler.getViewForPosition(position); recycler.bindViewToPosition(view, position); if (view != null) { RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight(), p.width); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), p.height); view.measure(childWidthSpec, childHeightSpec); measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin; measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin; recycler.recycleView(view); } } } 

    Yes the workaround shown in all answer is correct , that is we need to customize the linear layout manager to calculate the height of its child items dynamically at run time. But all answers not working as expected .Please the below answer for custom layout manger with all orientation support.

     public class MyLinearLayoutManager extends android.support.v7.widget.LinearLayoutManager { private static boolean canMakeInsetsDirty = true; private static Field insetsDirtyField = null; private static final int CHILD_WIDTH = 0; private static final int CHILD_HEIGHT = 1; private static final int DEFAULT_CHILD_SIZE = 100; private final int[] childDimensions = new int[2]; private final RecyclerView view; private int childSize = DEFAULT_CHILD_SIZE; private boolean hasChildSize; private int overScrollMode = ViewCompat.OVER_SCROLL_ALWAYS; private final Rect tmpRect = new Rect(); @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(Context context) { super(context); this.view = null; } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); this.view = null; } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(RecyclerView view) { super(view.getContext()); this.view = view; this.overScrollMode = ViewCompat.getOverScrollMode(view); } @SuppressWarnings("UnusedDeclaration") public MyLinearLayoutManager(RecyclerView view, int orientation, boolean reverseLayout) { super(view.getContext(), orientation, reverseLayout); this.view = view; this.overScrollMode = ViewCompat.getOverScrollMode(view); } public void setOverScrollMode(int overScrollMode) { if (overScrollMode < ViewCompat.OVER_SCROLL_ALWAYS || overScrollMode > ViewCompat.OVER_SCROLL_NEVER) throw new IllegalArgumentException("Unknown overscroll mode: " + overScrollMode); if (this.view == null) throw new IllegalStateException("view == null"); this.overScrollMode = overScrollMode; ViewCompat.setOverScrollMode(view, overScrollMode); } public static int makeUnspecifiedSpec() { return View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); } @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { final int widthMode = View.MeasureSpec.getMode(widthSpec); final int heightMode = View.MeasureSpec.getMode(heightSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); final boolean hasWidthSize = widthMode != View.MeasureSpec.UNSPECIFIED; final boolean hasHeightSize = heightMode != View.MeasureSpec.UNSPECIFIED; final boolean exactWidth = widthMode == View.MeasureSpec.EXACTLY; final boolean exactHeight = heightMode == View.MeasureSpec.EXACTLY; final int unspecified = makeUnspecifiedSpec(); if (exactWidth && exactHeight) { // in case of exact calculations for both dimensions let's use default "onMeasure" implementation super.onMeasure(recycler, state, widthSpec, heightSpec); return; } final boolean vertical = getOrientation() == VERTICAL; initChildDimensions(widthSize, heightSize, vertical); int width = 0; int height = 0; // it's possible to get scrap views in recycler which are bound to old (invalid) adapter entities. This // happens because their invalidation happens after "onMeasure" method. As a workaround let's clear the // recycler now (it should not cause any performance issues while scrolling as "onMeasure" is never // called whiles scrolling) recycler.clear(); final int stateItemCount = state.getItemCount(); final int adapterItemCount = getItemCount(); // adapter always contains actual data while state might contain old data (fe data before the animation is // done). As we want to measure the view with actual data we must use data from the adapter and not from the // state for (int i = 0; i < adapterItemCount; i++) { if (vertical) { if (!hasChildSize) { if (i < stateItemCount) { // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items // we will use previously calculated dimensions measureChild(recycler, i, widthSize, unspecified, childDimensions); } else { logMeasureWarning(i); } } height += childDimensions[CHILD_HEIGHT]; if (i == 0) { width = childDimensions[CHILD_WIDTH]; } if (hasHeightSize && height >= heightSize) { break; } } else { if (!hasChildSize) { if (i < stateItemCount) { // we should not exceed state count, otherwise we'll get IndexOutOfBoundsException. For such items // we will use previously calculated dimensions measureChild(recycler, i, unspecified, heightSize, childDimensions); } else { logMeasureWarning(i); } } width += childDimensions[CHILD_WIDTH]; if (i == 0) { height = childDimensions[CHILD_HEIGHT]; } if (hasWidthSize && width >= widthSize) { break; } } } if (exactWidth) { width = widthSize; } else { width += getPaddingLeft() + getPaddingRight(); if (hasWidthSize) { width = Math.min(width, widthSize); } } if (exactHeight) { height = heightSize; } else { height += getPaddingTop() + getPaddingBottom(); if (hasHeightSize) { height = Math.min(height, heightSize); } } setMeasuredDimension(width, height); if (view != null && overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS) { final boolean fit = (vertical && (!hasHeightSize || height < heightSize)) || (!vertical && (!hasWidthSize || width < widthSize)); ViewCompat.setOverScrollMode(view, fit ? ViewCompat.OVER_SCROLL_NEVER : ViewCompat.OVER_SCROLL_ALWAYS); } } private void logMeasureWarning(int child) { if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "Can't measure child #" + child + ", previously used dimensions will be reused." + "To remove this message either use #setChildSize() method or don't run RecyclerView animations"); } } private void initChildDimensions(int width, int height, boolean vertical) { if (childDimensions[CHILD_WIDTH] != 0 || childDimensions[CHILD_HEIGHT] != 0) { // already initialized, skipping return; } if (vertical) { childDimensions[CHILD_WIDTH] = width; childDimensions[CHILD_HEIGHT] = childSize; } else { childDimensions[CHILD_WIDTH] = childSize; childDimensions[CHILD_HEIGHT] = height; } } @Override public void setOrientation(int orientation) { // might be called before the constructor of this class is called //noinspection ConstantConditions if (childDimensions != null) { if (getOrientation() != orientation) { childDimensions[CHILD_WIDTH] = 0; childDimensions[CHILD_HEIGHT] = 0; } } super.setOrientation(orientation); } public void clearChildSize() { hasChildSize = false; setChildSize(DEFAULT_CHILD_SIZE); } public void setChildSize(int childSize) { hasChildSize = true; if (this.childSize != childSize) { this.childSize = childSize; requestLayout(); } } private void measureChild(RecyclerView.Recycler recycler, int position, int widthSize, int heightSize, int[] dimensions) { final View child; try { child = recycler.getViewForPosition(position); } catch (IndexOutOfBoundsException e) { if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "MyLinearLayoutManager doesn't work well with animations. Consider switching them off", e); } return; } final RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) child.getLayoutParams(); final int hPadding = getPaddingLeft() + getPaddingRight(); final int vPadding = getPaddingTop() + getPaddingBottom(); final int hMargin = p.leftMargin + p.rightMargin; final int vMargin = p.topMargin + p.bottomMargin; // we must make insets dirty in order calculateItemDecorationsForChild to work makeInsetsDirty(p); // this method should be called before any getXxxDecorationXxx() methods calculateItemDecorationsForChild(child, tmpRect); final int hDecoration = getRightDecorationWidth(child) + getLeftDecorationWidth(child); final int vDecoration = getTopDecorationHeight(child) + getBottomDecorationHeight(child); final int childWidthSpec = getChildMeasureSpec(widthSize, hPadding + hMargin + hDecoration, p.width, canScrollHorizontally()); final int childHeightSpec = getChildMeasureSpec(heightSize, vPadding + vMargin + vDecoration, p.height, canScrollVertically()); child.measure(childWidthSpec, childHeightSpec); dimensions[CHILD_WIDTH] = getDecoratedMeasuredWidth(child) + p.leftMargin + p.rightMargin; dimensions[CHILD_HEIGHT] = getDecoratedMeasuredHeight(child) + p.bottomMargin + p.topMargin; // as view is recycled let's not keep old measured values makeInsetsDirty(p); recycler.recycleView(child); } private static void makeInsetsDirty(RecyclerView.LayoutParams p) { if (!canMakeInsetsDirty) { return; } try { if (insetsDirtyField == null) { insetsDirtyField = RecyclerView.LayoutParams.class.getDeclaredField("mInsetsDirty"); insetsDirtyField.setAccessible(true); } insetsDirtyField.set(p, true); } catch (NoSuchFieldException e) { onMakeInsertDirtyFailed(); } catch (IllegalAccessException e) { onMakeInsertDirtyFailed(); } } private static void onMakeInsertDirtyFailed() { canMakeInsetsDirty = false; if (BuildConfig.DEBUG) { Log.w("MyLinearLayoutManager", "Can't make LayoutParams insets dirty, decorations measurements might be incorrect"); } } } 
    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.