Saya memiliki aplikasi yang mengelola koleksi buku (seperti daftar putar).
Saya ingin menampilkan daftar koleksi dengan RecyclerView vertikal dan di dalam setiap baris, daftar buku dalam RecyclerView horizontal.
Ketika saya mengatur layout_height dari RecyclerView horizontal ke 300dp, itu ditampilkan dengan benar tetapi ketika saya mengaturnya untuk wrap_content, itu tidak menampilkan apa pun. Saya perlu menggunakan wrap_content karena saya ingin dapat mengubah manajer tata letak secara terprogram untuk beralih antara tampilan vertikal dan horizontal.
Apakah Anda tahu apa yang saya lakukan salah?
Layout fragmen saya:
<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>
Tata letak elemen koleksi:
<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>
Item daftar buku:
<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>
Ini Adaptor Pengumpul saya:
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();
}
}
Dan akhirnya, adaptor Buku:
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();
}
}
Jawaban:
Memperbarui
Banyak masalah yang berkaitan dengan fitur ini dalam versi 23.2.0 telah diperbaiki di 23.2.1, perbarui untuk itu.
Dengan dirilisnya Perpustakaan Dukungan versi 23.2,
RecyclerView
sekarang mendukung itu!Perbarui
build.gradle
ke:atau versi apa pun di luar itu.
Ini dapat dinonaktifkan melalui
setAutoMeasurementEnabled()
jika perlu. Periksa secara detail di sini .sumber
23.2.1
RecyclerView tampaknya telah memperbaiki beberapa bug. Bekerja sangat baik untuk kita. Jika tidak, Anda bahkan dapat mencoba24.0.0-alpha1
Solusi @ user2302510 bekerja tidak sebaik yang Anda harapkan. Solusi penuh untuk orientasi dan perubahan data secara dinamis adalah:
sumber
Kode di atas tidak berfungsi dengan baik ketika Anda perlu membuat item Anda "wrap_content", karena ia mengukur tinggi dan lebar item dengan MeasureSpec.UNSPECIFIED. Setelah beberapa masalah, saya telah memodifikasi solusi itu sehingga sekarang item dapat berkembang. Satu-satunya perbedaan adalah bahwa ia menyediakan tinggi atau lebar orang tua MeasureSpec tergantung pada orientasi tata letak.
sumber
wrap_content
switch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
Manajer tata letak yang ada belum mendukung konten bungkus.
Anda dapat membuat LayoutManager baru yang memperluas yang sudah ada dan menimpa metode onMeasure untuk mengukur konten bungkus.
sumber
setHasFixedSize(boolean)
tidak berguna dengan manajer tata letak default? Bagaimanapun, senang mengetahui bahwa ini ada di peta jalan. Semoga ini segera keluar.Seperti yang disebutkan @ yiğit, Anda harus mengganti onMeasure (). @ User2302510 dan @DenisNek memiliki jawaban yang baik tetapi jika Anda ingin mendukung ItemDecoration Anda dapat menggunakan manajer tata letak kustom ini.
Dan jawaban lain tidak dapat menggulir ketika ada lebih banyak item daripada yang dapat ditampilkan di layar. Yang ini menggunakan implementasi standar onMeasure () ketika ada lebih banyak item dari ukuran layar.
Dan jika Anda ingin menggunakannya dengan GridLayoutManager hanya perluas dari GridLayoutManager dan ubah
untuk
sumber
gridlayoutmanager
danstaggeredgridlayoutmanager
mempertimbangkan rentang waktu dalam pikiran?PEMBARUAN Maret 2016
Oleh Android Support Library 23.2.1 dari versi perpustakaan dukungan. Jadi semua WRAP_CONTENT harus bekerja dengan benar.
Harap perbarui versi perpustakaan dalam file gradle.
Ini memungkinkan RecyclerView untuk mengukur sendiri berdasarkan pada ukuran isinya. Ini berarti bahwa skenario yang sebelumnya tidak tersedia, seperti menggunakan WRAP_CONTENT untuk dimensi RecyclerView, sekarang mungkin .
Anda akan diminta untuk memanggil setAutoMeasureEnabled (true)
Memperbaiki bug yang terkait dengan berbagai metode pengukuran spesifik dalam pembaruan
Periksa https://developer.android.com/topic/libraries/support-library/features.html
sumber
Jawaban ini didasarkan pada solusi yang diberikan oleh Denis Nek. Ini memecahkan masalah tidak memperhitungkan dekorasi seperti pembagi.
}
sumber
Alternatif untuk memperluas LayoutManager bisa dengan hanya mengatur ukuran tampilan secara manual.
Jumlah item per tinggi baris (jika semua item memiliki tinggi yang sama dan pemisah disertakan pada baris)
Masih merupakan solusi, tetapi untuk kasus dasar berfungsi.
sumber
Di sini saya telah menemukan solusinya: https://code.google.com/p/android/issues/detail?id=74772
Ini sama sekali bukan solusi saya. Saya baru saja menyalinnya dari sana, tetapi saya berharap ini akan membantu seseorang sebanyak itu membantu saya ketika menerapkan RecyclerView horizontal dan tinggi wrap_content (harus bekerja juga untuk yang vertikal dan lebar wrap_content)
Solusinya adalah memperluas LayoutManager dan mengganti metode onMeasure seperti yang disarankan @yigit.
Berikut adalah kode jika tautan mati:
sumber
Solusi yang digunakan dari @ sinan-kozak, kecuali memperbaiki beberapa bug. Secara khusus, kita tidak harus menggunakan
View.MeasureSpec.UNSPECIFIED
untuk kedua lebar dan tinggi saat memanggilmeasureScrapChild
seperti itu tidak akan benar memperhitungkan teks dibungkus anak. Sebagai gantinya, kami akan melewati mode lebar dan tinggi dari induk yang akan memungkinkan semuanya berfungsi untuk tata letak horizontal dan vertikal.`
sumber
RecyclerView
bersarang di aLinearLayout
. Tidak bekerja denganRelativeLayout
.Ya solusi yang ditunjukkan dalam semua jawaban sudah benar, yaitu kita perlu menyesuaikan manajer tata letak linier untuk menghitung ketinggian item turunannya secara dinamis saat dijalankan. Tetapi semua jawaban tidak berfungsi seperti yang diharapkan. Silakan jawaban di bawah ini untuk palang tata letak khusus dengan semua dukungan orientasi.
sumber
Pustaka Dukungan Android sekarang juga menangani properti WRAP_CONTENT. Impor saja ini di gradle Anda.
Dan dilakukan!
sumber
Berdasarkan karya Denis Nek, itu bekerja dengan baik jika jumlah lebar item lebih kecil dari ukuran wadah. selain itu, itu akan membuat recyclerview tidak dapat digulir dan hanya akan menampilkan sebagian dari data.
untuk mengatasi masalah ini, saya memodifikasi solusi alittle sehingga ia memilih min dari ukuran yang disediakan dan ukuran yang dihitung. Lihat di bawah:
sumber
Saya telah mencoba semua solusi, mereka sangat berguna tetapi ini hanya berfungsi dengan baik untuk saya
sumber
Cukup bungkus konten menggunakan RecyclerView dengan Layout Kotak
Gambar: Recycler sebagai tata letak GridView
Cukup gunakan GridLayoutManager seperti ini:
Anda dapat mengatur berapa banyak item yang akan muncul pada satu baris (ganti 3).
sumber