J'ai une application qui gère des collections de livres (comme des listes de lecture).
Je veux afficher une liste de collection avec un RecyclerView vertical et à l'intérieur de chaque ligne, une liste de livre dans un RecyclerView horizontal.
Lorsque je règle le layout_height du RecyclerView horizontal intérieur sur 300dp, il s'affiche correctement, mais lorsque je le règle sur wrap_content, il n'affiche rien. J'ai besoin d'utiliser wrap_content parce que je veux pouvoir changer le gestionnaire de disposition par programme pour basculer entre l'affichage vertical et horizontal.
Sais-tu ce que je fais de mal?
Ma mise en page 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>
Élément 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();
}
}
Réponses:
Mettre à jour
De nombreux problèmes liés à cette fonctionnalité dans la version 23.2.0 ont été corrigés dans 23.2.1, mettez à jour à la place.
Avec la sortie de la bibliothèque de support version 23.2,
RecyclerView
prend désormais en charge cela!Mettre
build.gradle
à jour vers:ou toute autre version au-delà.
Cela peut être désactivé via
setAutoMeasurementEnabled()
si nécessaire. Vérifiez en détail ici .la source
23.2.1
de RecyclerView semble avoir corrigé plusieurs bogues. Fonctionne très bien pour nous. Sinon, vous pouvez même essayer avec24.0.0-alpha1
La solution @ user2302510 ne fonctionne pas aussi bien que prévu. La solution de contournement complète pour les orientations et les modifications dynamiques des données est:
la source
Le code ci-dessus ne fonctionne pas bien lorsque vous devez rendre vos éléments "wrap_content", car il mesure à la fois la hauteur et la largeur des éléments avec MeasureSpec.UNSPECIFIED. Après quelques problèmes, j'ai modifié cette solution afin que les éléments puissent maintenant se développer. La seule différence est qu'il fournit aux parents la hauteur ou la largeur. MeasureSpec dépend de l'orientation de la disposition.
la source
wrap_content
switch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
Le gestionnaire de mise en page existant ne prend pas encore en charge le contenu enveloppant.
Vous pouvez créer un nouveau LayoutManager qui étend l'existant et remplace la méthode onMeasure pour mesurer le contenu d'habillage.
la source
setHasFixedSize(boolean)
inutile avec les gestionnaires de mise en page par défaut? Bref, c'est bien de savoir que c'est sur la feuille de route. J'espère qu'il sortira bientôt.Comme @ yiğit l'a mentionné, vous devez remplacer onMeasure (). @ 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 défiler lorsqu'il y a plus d'éléments que ce qui peut être affiché à l'écran. Celui-ci utilise l'implémentation par défaut de onMeasure () lorsqu'il y a plus d'éléments que la taille de l'écran.
Et si vous voulez l'utiliser avec GridLayoutManager l'étend simplement de GridLayoutManager et changez
à
la source
gridlayoutmanager
et enstaggeredgridlayoutmanager
tenant compte du nombre de travées?MISE À JOUR Mars 2016
Par Android Support Library 23.2.1 d'une version de bibliothèque de support. Donc, tout WRAP_CONTENT devrait fonctionner correctement.
Veuillez mettre à jour la version d'une bibliothèque dans le fichier gradle.
Cela permet à un RecyclerView de se dimensionner en fonction de la taille de son contenu. Cela signifie que des scénarios précédemment indisponibles, tels que l' utilisation de WRAP_CONTENT pour une dimension de RecyclerView, sont désormais possibles .
vous devrez appeler setAutoMeasureEnabled (true)
Correction de bugs liés à diverses méthodes de spécification de mesure dans la mise à jour
Vérifiez https://developer.android.com/topic/libraries/support-library/features.html
la source
Cette réponse est basée sur la solution donnée par Denis Nek. Cela résout le problème de ne pas prendre en compte les décorations comme les diviseurs.
}
la source
Une alternative pour étendre LayoutManager peut simplement être de définir manuellement la taille de la vue.
Nombre d'éléments par hauteur de ligne (si tous les éléments ont la même hauteur et que le séparateur est inclus sur la ligne)
Est toujours une solution de contournement, mais pour les cas de base, cela fonctionne.
la source
Ici, j'ai trouvé 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 que cela m'a aidé lors de la mise en œuvre de RecyclerView horizontal et de la hauteur wrap_content (devrait fonctionner aussi pour un vertical et wrap_content width)
La solution consiste à étendre le LayoutManager et à remplacer sa méthode onMeasure comme @yigit le suggère.
Voici le code au cas où le lien meurt:
la source
Solution utilisée de @ sinan-kozak, sauf correction de quelques bugs. Plus précisément, nous ne devons pas utiliser
View.MeasureSpec.UNSPECIFIED
à la fois la largeur et la hauteur lors de l'appel,measureScrapChild
car cela ne tiendra pas correctement compte du texte enveloppé dans l'enfant. Au lieu de cela, nous passerons par les modes largeur et hauteur du parent, ce qui permettra aux choses de fonctionner à la fois pour les dispositions horizontales et verticales.»
la source
RecyclerView
est imbriqué dans un fichierLinearLayout
. Ne fonctionne pas avecRelativeLayout
.Oui, la solution de contournement indiquée dans toutes les réponses est correcte, c'est-à-dire que nous devons personnaliser le gestionnaire de disposition linéaire pour calculer la hauteur de ses éléments enfants de manière dynamique au moment de l'exécution. Mais toutes les réponses ne fonctionnent pas comme prévu. Veuillez la réponse ci-dessous pour le gestionnaire de mise en page personnalisé avec tout le support d'orientation.
la source
La bibliothèque de support Android gère désormais également la propriété WRAP_CONTENT. Importez simplement ceci dans votre gradle.
Et.. Voila!
la source
Basé sur le travail de Denis Nek, cela fonctionne bien si la somme des largeurs de l'article est inférieure à la taille du conteneur. à part cela, cela rendra la vue de recyclage non défilable et affichera uniquement un sous-ensemble des données.
pour résoudre ce problème, j'ai modifié un peu la solution pour qu'elle choisisse le min de la taille fournie et la taille calculée. voir ci-dessous:
la source
J'ai essayé toutes les solutions, elles sont très utiles mais cela ne fonctionne que pour moi
la source
Emballez simplement le contenu à l'aide de RecyclerView avec la disposition en grille
Image: Recycler en tant que disposition GridView
Utilisez simplement le GridLayoutManager comme ceci:
Vous pouvez définir le nombre d'éléments qui doivent apparaître sur une ligne (remplacez le 3).
la source