Лично мне не нравится создавать подкласс RecyclerView для этого, потому что мне кажется, что GridLayoutManager несет ответственность за определение количества промежутков. Итак, после некоторого поиска исходного кода Android для RecyclerView и GridLayoutManager я написал свой собственный расширенный класс GridLayoutManager, который выполняет эту работу:
public class GridAutofitLayoutManager extends GridLayoutManager
{
private int columnWidth;
private boolean isColumnWidthChanged = true;
private int lastWidth;
private int lastHeight;
public GridAutofitLayoutManager(@NonNull final Context context, final int columnWidth) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
public GridAutofitLayoutManager(
@NonNull final Context context,
final int columnWidth,
final int orientation,
final boolean reverseLayout) {
/* Initially set spanCount to 1, will be changed automatically later. */
super(context, 1, orientation, reverseLayout);
setColumnWidth(checkedColumnWidth(context, columnWidth));
}
private int checkedColumnWidth(@NonNull final Context context, final int columnWidth) {
if (columnWidth <= 0) {
/* Set default columnWidth value (48dp here). It is better to move this constant
to static constant on top, but we need context to convert it to dp, so can't really
do so. */
columnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
context.getResources().getDisplayMetrics());
}
return columnWidth;
}
public void setColumnWidth(final int newColumnWidth) {
if (newColumnWidth > 0 && newColumnWidth != columnWidth) {
columnWidth = newColumnWidth;
isColumnWidthChanged = true;
}
}
@Override
public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) {
final int width = getWidth();
final int height = getHeight();
if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
final int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = width - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = height - getPaddingTop() - getPaddingBottom();
}
final int spanCount = Math.max(1, totalSpace / columnWidth);
setSpanCount(spanCount);
isColumnWidthChanged = false;
}
lastWidth = width;
lastHeight = height;
super.onLayoutChildren(recycler, state);
}
}
На самом деле я не помню, почему я решил установить счетчик промежутков в onLayoutChildren, я написал этот класс некоторое время назад. Но дело в том, что мы должны сделать это после того, как будет измерена точка зрения. так что мы можем получить его высоту и ширину.
РЕДАКТИРОВАТЬ 1: Исправить ошибку в коде, вызвавшую неправильную установку счетчика диапазона. Благодарим пользователя @Elyees Abouda за сообщение и предложение решения .
РЕДАКТИРОВАТЬ 2: Небольшой рефакторинг и исправление краевого случая с ручной обработкой изменений ориентации. Спасибо пользователю @tatarize за сообщение и предложение решения .
LayoutManager
«s работа закладывать детей вне , а неRecyclerView
» sgetWidth()
илиgetHeight()
равно 0 до создания представления, которое будет иметь неправильный spanCount (1, поскольку totalSpace будет <= 0). В этом случае я добавил игнорирование setSpanCount. (onLayoutChildren
будет называться позже)Я выполнил это с помощью наблюдателя дерева представлений, чтобы получить ширину повторного просмотра после рендеринга, а затем получить фиксированные размеры представления моей карты из ресурсов, а затем установить счетчик диапазона после выполнения моих расчетов. Это действительно применимо, только если отображаемые элементы имеют фиксированную ширину. Это помогло мне автоматически заполнить сетку независимо от размера или ориентации экрана.
источник
ArrayIndexOutOfBoundsException
( на android.support.v7.widget.GridLayoutManager.layoutChunk (GridLayoutManager.java:361) ) при прокрутке файлаRecyclerView
.removeGlobalOnLayoutListener()
не рекомендуется на уровне API 16. используйтеremoveOnGlobalLayoutListener()
вместо него. Документация .Что ж, это то, что я использовал, довольно простой, но он выполняет свою работу за меня. Этот код в основном получает ширину экрана в виде провалов, а затем делится на 300 (или любую другую ширину, которую вы используете для макета вашего адаптера). Таким образом, телефоны меньшего размера с шириной падения 300-500 отображают только один столбец, планшеты - 2-3 столбца и т. Д. Просто, без проблем и без недостатков, насколько я могу судить.
источник
Я расширил RecyclerView и переопределил метод onMeasure.
Я установил ширину элемента (переменную-член) как можно раньше, со значением по умолчанию 1. Это также обновляется при изменении конфигурации. Теперь у него будет столько строк, сколько поместится в портретной, альбомной ориентации, телефоне / планшете и т. Д.
источник
Я публикую это на всякий случай, если кто-то получит странную ширину столбца, как в моем случае.
Я не могу комментировать ответ @ s-mark из-за моей низкой репутации. Я применил свое решение решения , но я получил некоторую странную ширину столбца, поэтому я изменил checkedColumnWidth функции следующим образом :
Путем преобразования заданной ширины столбца в DP устранена проблема.
источник
Чтобы учесть изменение ориентации в ответе s- mark, я добавил проверку изменения ширины (ширину из getWidth (), а не ширину столбца).
источник
Решение, за которое проголосовали, в порядке, но обрабатывает входящие значения как пиксели, что может сбить вас с толку, если вы жестко кодируете значения для тестирования и предполагаете dp. Самый простой способ - это, вероятно, поместить ширину столбца в измерение и прочитать ее при настройке GridAutofitLayoutManager, который автоматически преобразует dp в правильное значение пикселя:
источник
Когда вы создаете GridLayoutManager, вам нужно знать, сколько столбцов будет при минимальном размере imageView:
После этого вам нужно изменить размер imageView в адаптере, если у вас есть место в столбце. Вы можете отправить newImageViewSize, а затем inisilize адаптер из активности, где вы вычисляете количество экранов и столбцов:
Он работает в обоих направлениях. По вертикали у меня 2 столбца, а по горизонтали - 4 столбца. Результат: https://i.stack.imgur.com/WHvyD.jpg
источник
Я делаю вывод выше ответов здесь
источник
Это класс s.maks с небольшими исправлениями, связанными с изменением размера самого recyclerview. Например, когда вы имеете дело с изменением ориентации самостоятельно (в манифесте
android:configChanges="orientation|screenSize|keyboardHidden"
), или по какой-либо другой причине recyclerview может изменить размер без изменения mColumnWidth. Я также изменил значение int, которое должно быть ресурсом этого размера, и разрешил конструктору без ресурса, а затем setColumnWidth сделать это самостоятельно.источник
Задайте для spanCount большое число (максимальное количество столбцов) и установите настраиваемый SpanSizeLookup для GridLayoutManager.
Это немного некрасиво, но работает.
Я думаю, что менеджер типа AutoSpanGridLayoutManager был бы лучшим решением, но я не нашел ничего подобного.
РЕДАКТИРОВАТЬ: есть ошибка, на каком-то устройстве он добавляет пустое пространство справа
источник
getSpanSize
возвращает 3, будет пробел, потому что вы не заполняете диапазон.Вот соответствующие части оболочки, которые я использовал для автоматического определения количества диапазонов. Вы инициализировать его по телефону
setGridLayoutManager
с R.layout.my_grid_item ссылкой, и выясняет , сколько из них может поместиться на каждой строке.источник