Ich habe eine Anwendung, die Sammlungen von Büchern (wie Wiedergabelisten) verwaltet.
Ich möchte eine Liste der Sammlungen mit einer vertikalen RecyclerView und in jeder Zeile eine Liste der Bücher in einer horizontalen RecyclerView anzeigen.
Wenn ich die layout_height der inneren horizontalen RecyclerView auf 300dp setze, wird sie korrekt angezeigt, aber wenn ich sie auf wrap_content setze, wird nichts angezeigt. Ich muss wrap_content verwenden, weil ich den Layout-Manager programmgesteuert ändern möchte, um zwischen vertikaler und horizontaler Anzeige zu wechseln.
Weißt du was ich falsch mache?
Mein Fragment-Layout:
<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>
Layout der Sammlungselemente:
<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>
Buchlistenelement:
<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>
Hier ist mein Sammlungsadapter:
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();
}
}
Und schließlich der Buchadapter:
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();
}
}
Antworten:
Aktualisieren
Viele Probleme im Zusammenhang mit dieser Funktion in Version 23.2.0 wurden in 23.2.1 behoben. Aktualisieren Sie stattdessen darauf.
Mit der Veröffentlichung der Support Library Version 23.2 wird dies
RecyclerView
jetzt unterstützt!Update
build.gradle
auf:oder eine darüber hinausgehende Version.
Dies kann
setAutoMeasurementEnabled()
bei Bedarf über deaktiviert werden. Schauen Sie im Detail hier .quelle
23.2.1
von RecyclerView scheint mehrere Fehler behoben zu haben. Funktioniert sehr gut für uns. Wenn nicht, können Sie es sogar mit24.0.0-alpha1
@ user2302510 Lösung funktioniert nicht so gut wie erwartet. Die vollständige Problemumgehung für Orientierungen und dynamische Datenänderungen lautet:
quelle
Der obige Code funktioniert nicht gut, wenn Sie Ihre Elemente zu "wrap_content" machen müssen, da er mit MeasureSpec.UNSPECIFIED sowohl die Höhe als auch die Breite der Elemente misst. Nach einigen Problemen habe ich diese Lösung so geändert, dass jetzt Elemente erweitert werden können. Der einzige Unterschied besteht darin, dass die Größe oder Breite der Eltern angegeben wird. MeasureSpec hängt von der Ausrichtung des Layouts ab.
quelle
wrap_content
switch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
Der vorhandene Layout-Manager unterstützt Wrap-Inhalte noch nicht.
Sie können einen neuen LayoutManager erstellen, der den vorhandenen erweitert und die onMeasure-Methode überschreibt, um den Wrap-Inhalt zu messen.
quelle
setHasFixedSize(boolean)
die Standard-Layout-Manager nicht unbrauchbar? Wie auch immer, schön zu wissen, dass es auf der Roadmap steht. Hoffe es kommt bald raus.Wie @ yiğit erwähnt, müssen Sie onMeasure () überschreiben. Sowohl @ user2302510 als auch @DenisNek haben gute Antworten. Wenn Sie jedoch ItemDecoration unterstützen möchten, können Sie diesen benutzerdefinierten Layout-Manager verwenden.
Andere Antworten können nicht gescrollt werden, wenn mehr Elemente vorhanden sind, als auf dem Bildschirm angezeigt werden können. In diesem Fall wird die Standardimplementierung von onMeasure () verwendet, wenn mehr Elemente als die Bildschirmgröße vorhanden sind.
Und wenn Sie es mit GridLayoutManager verwenden möchten, erweitern Sie es einfach von GridLayoutManager und ändern Sie es
zu
quelle
gridlayoutmanager
undstaggeredgridlayoutmanager
die Anzahl der Spannen berücksichtigen?UPDATE März 2016
Von Android Support Library 23.2.1 einer Support Library-Version. Alle WRAP_CONTENT sollten also korrekt funktionieren.
Bitte aktualisieren Sie die Version einer Bibliothek in der Gradle-Datei.
Auf diese Weise kann sich ein RecyclerView basierend auf der Größe seines Inhalts selbst anpassen. Dies bedeutet, dass zuvor nicht verfügbare Szenarien wie die Verwendung von WRAP_CONTENT für eine Dimension der RecyclerView jetzt möglich sind .
Sie müssen setAutoMeasureEnabled (true) aufrufen.
Fehler im Zusammenhang mit verschiedenen Messspezifikationsmethoden im Update behoben
Prüfen https://developer.android.com/topic/libraries/support-library/features.html
quelle
Diese Antwort basiert auf der Lösung von Denis Nek. Es löst das Problem, Dekorationen wie Trennwände nicht zu berücksichtigen.
}}
quelle
Eine Alternative zum Erweitern von LayoutManager kann einfach die Größe der Ansicht manuell festlegen.
Anzahl der Elemente pro Zeilenhöhe (wenn alle Elemente dieselbe Höhe haben und das Trennzeichen in der Zeile enthalten ist)
Ist immer noch eine Problemumgehung, aber für grundlegende Fälle funktioniert es.
quelle
Hier habe ich eine Lösung gefunden: https://code.google.com/p/android/issues/detail?id=74772
Es ist in keiner Weise meine Lösung. Ich habe es gerade von dort kopiert, aber ich hoffe, es wird jemandem genauso helfen, wie es mir bei der Implementierung von horizontalem RecyclerView und wrap_content height geholfen hat (sollte auch für vertikale und wrap_content width funktionieren)
Die Lösung besteht darin, den LayoutManager zu erweitern und seine onMeasure-Methode wie von @yigit vorgeschlagen zu überschreiben.
Hier ist der Code für den Fall, dass der Link stirbt:
quelle
Verwendete Lösung von @ sinan-kozak, außer ein paar Fehler behoben. Insbesondere sollten wir nicht verwenden ,
View.MeasureSpec.UNSPECIFIED
für sowohl die Breite und Höhe beim AufrufmeasureScrapChild
als das wird nicht richtig für verpackte Text - Konto in dem Kind. Stattdessen werden wir die Breiten- und Höhenmodi des übergeordneten Elements durchlaufen, damit die Dinge sowohl für horizontale als auch für vertikale Layouts funktionieren können.`
quelle
RecyclerView
in a verschachtelt istLinearLayout
. Funktioniert nicht mitRelativeLayout
.Ja, die in allen Antworten angezeigte Problemumgehung ist korrekt. Das heißt, wir müssen den linearen Layout-Manager anpassen, um die Höhe seiner untergeordneten Elemente zur Laufzeit dynamisch zu berechnen. Aber alle Antworten funktionieren nicht wie erwartet. Bitte beachten Sie die folgende Antwort für benutzerdefinierte Layout-Manager mit jeglicher Orientierungsunterstützung.
quelle
Die Android Support Library verarbeitet jetzt auch die WRAP_CONTENT-Eigenschaft. Importieren Sie dies einfach in Ihren Gradle.
Und fertig!
quelle
Basierend auf der Arbeit von Denis Nek funktioniert es gut, wenn die Summe der Artikelbreiten kleiner als die Größe des Containers ist. Andernfalls wird die Recycling-Ansicht nicht scrollbar und es wird nur eine Teilmenge der Daten angezeigt.
Um dieses Problem zu lösen, habe ich die Lösung ein wenig modifiziert, so dass sie die min der bereitgestellten Größe und der berechneten Größe wählt. siehe unten:
quelle
Ich habe alle Lösungen ausprobiert, sie sind sehr nützlich, aber das funktioniert nur gut für mich
quelle
Wickeln Sie den Inhalt einfach mit RecyclerView in das Rasterlayout ein
Bild: Recycler als GridView-Layout
Verwenden Sie einfach den GridLayoutManager wie folgt:
Sie können festlegen, wie viele Elemente in einer Zeile angezeigt werden sollen (ersetzen Sie die 3).
quelle