Pessoalmente, não gosto de criar uma subclasse de RecyclerView para isso, porque, para mim, parece que é responsabilidade do GridLayoutManager detectar a contagem de amplitude. Então, depois de pesquisar o código-fonte do Android para RecyclerView e GridLayoutManager, escrevi minha própria classe GridLayoutManager estendida que faz o trabalho:
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);
}
}
Na verdade, não me lembro por que escolhi definir a contagem de amplitude em onLayoutChildren, escrevi esta classe há algum tempo. Mas a questão é que precisamos fazer isso depois que a visualização for medida. para que possamos obter sua altura e largura.
EDIT 1: Corrigir erro no código causado pela configuração incorreta da contagem de amplitude. Obrigado ao usuário @Elyees Abouda por relatar e sugerir soluções .
EDIT 2: Algumas pequenas refatorações e correção de casos extremos com manipulação manual de mudanças de orientação. Obrigado ao usuário @tatarize por relatar e sugerir a solução .
LayoutManager
's trabalho para colocar as crianças para fora e nãoRecyclerView
' sgetWidth()
ougetHeight()
é 0 antes de a visualização ser criada, o que resultará em um spanCount errado (1, pois totalSpace será <= 0). O que adicionei foi ignorar setSpanCount neste caso. (onLayoutChildren
será chamada novamente mais tarde)Eu fiz isso usando um observador de árvore de visualização para obter a largura da recylcerview uma vez renderizada e, em seguida, obter as dimensões fixas de minha visualização de cartão a partir dos recursos e definir a contagem de amplitude após fazer meus cálculos. Só é realmente aplicável se os itens que você está exibindo tiverem uma largura fixa. Isso me ajudou a preencher automaticamente a grade, independentemente do tamanho ou orientação da tela.
fonte
ArrayIndexOutOfBoundsException
( em android.support.v7.widget.GridLayoutManager.layoutChunk (GridLayoutManager.java:361) ) ao rolar oRecyclerView
.removeGlobalOnLayoutListener()
está obsoleto na API de nível 16. use em seuremoveOnGlobalLayoutListener()
lugar. Documentação .Bem, isso é o que eu usei, bastante básico, mas faz o trabalho para mim. Este código basicamente obtém a largura da tela em diminuições e então divide por 300 (ou qualquer largura que você esteja usando para o layout do adaptador). Portanto, telefones menores com largura de mergulho de 300-500 exibem apenas uma coluna, tablets de 2-3 colunas etc. Simples, sem complicações e sem desvantagens, pelo que posso ver.
fonte
Estendi o RecyclerView e substituí o método onMeasure.
Eu defino uma largura de item (variável de membro) o mais cedo que posso, com um padrão de 1. Isso também atualiza a configuração alterada. Agora, isso terá quantas linhas cabem em retrato, paisagem, telefone / tablet etc.
fonte
Estou postando isso apenas no caso de alguém ficar com uma largura de coluna estranha como no meu caso.
Não posso comentar a resposta de @s-marks devido à minha baixa reputação. I aplicou sua solução solução , mas eu tenho alguns largura da coluna estranho, então eu modificado checkedColumnWidth função da seguinte forma:
Ao converter a largura da coluna dada em DP corrigiu o problema.
fonte
Para acomodar a mudança de orientação na resposta das marcas s, adicionei uma verificação na mudança de largura (largura de getWidth (), não largura da coluna).
fonte
A solução upvoted é boa, mas trata os valores de entrada como pixels, o que pode atrapalhar se você estiver codificando valores para teste e assumindo dp. Provavelmente, a maneira mais fácil é colocar a largura da coluna em uma dimensão e lê-la ao configurar o GridAutofitLayoutManager, que converterá automaticamente dp para o valor correto do pixel:
fonte
Ao criar GridLayoutManager, você precisa saber quantas colunas terão com o tamanho mínimo de imageView:
Depois disso, você precisa redimensionar imageView no adaptador se houver espaço na coluna. Você pode enviar newImageViewSize e, em seguida, inisilizar o adaptador da atividade, onde você calcula a contagem de tela e coluna:
Funciona em ambas as orientações. Na vertical tenho 2 colunas e na horizontal - 4 colunas. O resultado: https://i.stack.imgur.com/WHvyD.jpg
fonte
Concluo acima das respostas aqui
fonte
Esta é a classe s.maks com uma pequena correção para quando o próprio recyclerview muda de tamanho. Por exemplo, quando você mesmo lida com as mudanças de orientação (no manifesto
android:configChanges="orientation|screenSize|keyboardHidden"
), ou por algum outro motivo, a visualização de reciclagem pode mudar de tamanho sem alterar mColumnWidth. Eu também mudei o valor int necessário para ser o recurso do tamanho e permiti que um construtor sem recurso, então, setColumnWidth fizesse isso você mesmo.fonte
Defina spanCount como um número grande (que é o número máximo de colunas) e defina um SpanSizeLookup personalizado para GridLayoutManager.
É um pouco feio, mas funciona.
Acho que um gerenciador como AutoSpanGridLayoutManager seria a melhor solução, mas não encontrei nada parecido.
EDIT: Há um bug, em algum dispositivo adiciona espaço em branco à direita
fonte
getSpanSize
retornar 3, haverá um espaço porque você não está preenchendo o intervalo.Aqui estão as partes relevantes de um wrapper que estou usando para detectar automaticamente a contagem de amplitude. Você o inicializa chamando
setGridLayoutManager
com uma referência R.layout.my_grid_item , e ele descobre quantos deles cabem em cada linha.fonte