У меня есть приложение, которое управляет коллекциями книг (например, плейлистами).
Я хочу отобразить список коллекций с вертикальным RecyclerView и внутри каждой строки, список книг в горизонтальном RecyclerView.
Когда я устанавливаю layout_height внутреннего горизонтального RecyclerView в 300dp, он отображается правильно, но когда я устанавливаю его в wrap_content, он ничего не отображает. Мне нужно использовать wrap_content, потому что я хочу иметь возможность программно изменять менеджер макетов для переключения между вертикальным и горизонтальным отображением.
Ты знаешь, что я делаю не так?
Макет моего фрагмента:
<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>
Макет элемента коллекции:
<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>
Элемент списка книг:
<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>
Вот мой коллекционный адаптер:
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();
}
}
И, наконец, адаптер 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();
}
}
Ответы:
Обновить
Многие проблемы, связанные с этой функцией в версии 23.2.0, были исправлены в 23.2.1, обновите ее.
С выпуском библиотеки поддержки версии 23.2
RecyclerView
теперь это поддерживается!Обновление
build.gradle
до:или любая другая версия.
Это может быть отключено через
setAutoMeasurementEnabled()
при необходимости. Проверьте подробно здесь .источник
23.2.1
RecyclerView, похоже, исправила несколько ошибок. Работает очень хорошо для нас. Если нет, вы даже можете попробовать с24.0.0-alpha1
@ user2302510 решение работает не так хорошо, как вы могли ожидать. Полный обходной путь как для ориентации, так и для динамического изменения данных:
источник
Приведенный выше код не работает хорошо, когда вам нужно сделать ваши элементы "wrap_content", потому что он измеряет высоту и ширину элементов с помощью MeasureSpec.UNSPECIFIED. После некоторых проблем я изменил это решение, чтобы теперь предметы могли расширяться. Единственное отличие состоит в том, что он предоставляет родителям высоту или ширину. MeasureSpec зависит от ориентации макета.
источник
wrap_content
switch (heightMode) { case View.MeasureSpec.AT_MOST: if (height < heightSize) break; case View.MeasureSpec.EXACTLY: height = heightSize; case View.MeasureSpec.UNSPECIFIED: }
Существующий менеджер макетов пока не поддерживает перенос содержимого.
Вы можете создать новый LayoutManager, который расширяет существующий и переопределяет метод onMeasure для измерения содержимого переноса.
источник
setHasFixedSize(boolean)
бесполезным с менеджерами по умолчанию по умолчанию? Во всяком случае, приятно знать, что это на дорожной карте. Надеюсь, это скоро выйдет.Как уже упоминалось @ yiğit, вам нужно переопределить onMeasure (). И @ user2302510, и @DenisNek имеют хорошие ответы, но если вы хотите поддержать ItemDecoration, вы можете использовать этот менеджер пользовательских макетов.
И другие ответы не могут прокручиваться, если на экране больше элементов, чем можно отобразить. Этот использует стандартную реализацию onMeasure (), когда элементов больше, чем размер экрана.
И если вы хотите использовать его с GridLayoutManager, просто расширяйте его из GridLayoutManager и меняйте
в
источник
gridlayoutmanager
иstaggeredgridlayoutmanager
учитывая количество пролетов?ОБНОВЛЕНИЕ март 2016
По библиотеке поддержки Android 23.2.1 версии библиотеки поддержки. Так что все WRAP_CONTENT должны работать правильно.
Пожалуйста, обновите версию библиотеки в файле Gradle.
Это позволяет RecyclerView изменять размер в зависимости от размера его содержимого. Это означает, что ранее недоступные сценарии, такие как использование WRAP_CONTENT для измерения RecyclerView, теперь возможны .
вам нужно будет вызвать setAutoMeasureEnabled (true)
Исправлены ошибки, связанные с различными методами измерения мер в обновлении
Проверьте https://developer.android.com/topic/libraries/support-library/features.html
источник
Этот ответ основан на решении, которое дал Денис Нек. Это решает проблему не принимать во внимание украшения, такие как разделители.
}
источник
Альтернативой расширению LayoutManager можно просто установить размер представления вручную.
Количество элементов на высоту строки (если все элементы имеют одинаковую высоту и разделитель включен в строку)
Это все еще обходной путь, но для основных случаев это работает.
источник
Здесь я нашел решение: https://code.google.com/p/android/issues/detail?id=74772
Это никоим образом не мое решение. Я только что скопировал это оттуда, но я надеюсь, что это поможет кому-то так же, как это помогло мне при реализации горизонтального RecyclerView и wrap_content height (должно работать также для вертикального и ширины wrap_content)
Решение состоит в том, чтобы расширить LayoutManager и переопределить его метод onMeasure, как предложено @yigit.
Вот код на случай, если ссылка умирает:
источник
Использовано решение от @ sinan-kozak, кроме исправленных нескольких ошибок. В частности, мы не должны использовать
View.MeasureSpec.UNSPECIFIED
для обоих ширины и высоты при вызове ,measureScrapChild
как это не будет должным образом учитывать завернутые текста ребенка. Вместо этого мы пройдем через режимы ширины и высоты от родителя, что позволит работать как для горизонтальной, так и для вертикальной компоновки.`
источник
RecyclerView
вложено вLinearLayout
. Не работает сRelativeLayout
.Да, обходной путь, показанный во всех ответах, является правильным, то есть нам нужно настроить менеджер линейного макета для динамического вычисления высоты его дочерних элементов во время выполнения. Но все ответы не работают должным образом. Пожалуйста, ответьте ниже для пользовательского макета менеджера со всей поддержкой ориентации.
источник
Библиотека поддержки Android теперь также обрабатывает свойство WRAP_CONTENT. Просто импортируйте это в ваш gradle.
И сделано!
источник
Основываясь на работе Дениса Нека, он работает хорошо, если сумма ширины элемента меньше, чем размер контейнера. кроме этого, он сделает невидимым прокручиваемое окно просмотра и покажет только подмножество данных.
Чтобы решить эту проблему, я изменил решение так, чтобы оно выбрало минимум предоставленного размера и вычисленного размера. увидеть ниже:
источник
Я перепробовал все решения, они очень полезны, но у меня это работает нормально
источник
Просто оберните содержимое с помощью RecyclerView с Grid Layout
Изображение: Recycler как макет GridView
Просто используйте GridLayoutManager следующим образом:
Вы можете установить, сколько элементов должно отображаться в строке (замените 3).
источник