android - usar - RecyclerView: elementos fuera de pantalla omitiendo el primer pase de diseño
son los elementos qué construyen un recyclerview (3)
Verifique si ha setHasStableIds(true)
; si lo hace, debe anular el getItemId(int position)
para especificar ids para cada vista. Si no lo hace, las siguientes llamadas a onBindViewholders()
no se activarán y su vista no se actualizará.
Estoy teniendo problemas con uno de mis adaptadores. Tengo una selección de imágenes que se muestran en una cuadrícula como la siguiente:
Esto funciona bien cuando tengo una pantalla con valor de elementos y todos se muestran bien. El problema aparece cuando hay más elementos para desplazarse hacia abajo. Inicialmente, cuando me desplazo hacia abajo, los elementos aparecen en blanco, los contenedores GridLayout
están allí y se ejecuta el código para mostrar las imágenes, pero no aparecen en la pantalla como se muestra aquí:
Si me desplazo hacia atrás, vuelvo a bajar, las imágenes aparecen exactamente donde deberían estar:
Parece que necesito actualizar el diseño o decirle al procesador que algo ha cambiado, pero nada de lo que intento parece hacer nada. Intenté llamar a requestLayout
e invalidate
pero parece que no resuelve el problema. También registré un GlobalOnLayoutListener, pero no me fue mejor.
Aquí está el código relevante:
Esto es de mi método onBindViewHolder
final GridLayout grid = ((KKAlbumViewHolder) holder).mGridLayout;
grid.removeAllViews();
int width = grid.getWidth();
if(width > 0) {
albumHolder.setPreviewImages(posts);
}else {
albumHolder.itemView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
albumHolder.itemView.removeOnLayoutChangeListener(this);
albumHolder.setPreviewImages(posts);
albumHolder.mGridLayout.invalidate();
}
});
}
Este es el método que uso para diseñar las imágenes. Básicamente coloca las imágenes en una cuadrícula irregular, por lo que si hay una imagen larga o alta, esa imagen intentará ocupar una fila completa o una columna completa, dependiendo de qué espacio es libre. La razón por la que configuro el ancho y la altura del .fit()
es para que picasso no se queje del método .fit()
:
public void setPreviewImages(ArrayList<KKPost> posts) {
Picasso picasso = Picasso.with(itemView.getContext());
int space = 0;
int tl = 1 << 0;
int tr = 1 << 1;
int bl = 1 << 2;
int br = 1 << 3;
int lCol = tl | bl;
int rCol = tr | br;
int tRow = tl | tr;
int bRow = bl | br;
int full = lCol | rCol;
boolean hasLongImage = false;
for(KKPost post : posts) {
if(space == full){
break;
}
int margin = 2;
ImageView view = new ImageView(itemView.getContext());
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
view.setBackgroundColor(Color.LTGRAY);
if(posts.size() < 4){
param.columnSpec = GridLayout.spec(0, 2);
param.rowSpec = GridLayout.spec(0, 2);
param.width = mGridLayout.getWidth();
param.height = mGridLayout.getHeight();
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
break;
}
if (post.aspectRatio >= 1.2 && !hasLongImage) {
//landscape
if((space & tRow) == 0){
param.rowSpec = GridLayout.spec(0, 1);
param.columnSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth();
param.bottomMargin = margin;
space |= tRow;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
if((space & bRow) == 0){
param.rowSpec = GridLayout.spec(1, 1);
param.columnSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth();
param.topMargin = margin;
space |= bRow;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
} else if (post.aspectRatio <= 0.8 && post.aspectRatio > 0 && !hasLongImage) {
//portrait
if((space & lCol) == 0){
param.columnSpec = GridLayout.spec(0, 1);
param.rowSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight();
param.width = mGridLayout.getWidth()/2;
param.rightMargin = margin;
space |= lCol;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
if((space & rCol) == 0){
param.columnSpec = GridLayout.spec(1, 1);
param.rowSpec = GridLayout.spec(0, 2);
param.height = mGridLayout.getHeight();
param.width = mGridLayout.getWidth()/2;
param.leftMargin = margin;
space |= rCol;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
hasLongImage = true;
continue;
}
}
//square or no space or unknown
if((space & tl) == 0){
param.columnSpec = GridLayout.spec(0,1);
param.rowSpec = GridLayout.spec(0,1);
space |= tl;
param.bottomMargin = margin;
param.rightMargin = margin;
}else
if((space & tr) == 0) {
param.columnSpec = GridLayout.spec(1,1);
param.rowSpec = GridLayout.spec(0,1);
space |= tr;
param.bottomMargin = margin;
param.leftMargin = margin;
}else
if((space & bl) == 0){
param.columnSpec = GridLayout.spec(0,1);
param.rowSpec = GridLayout.spec(1,1);
space |= bl;
param.rightMargin = margin;
param.topMargin = margin;
}else
if((space & br) == 0){
param.columnSpec = GridLayout.spec(1,1);
param.rowSpec = GridLayout.spec(1,1);
space |= br;
param.leftMargin = margin;
param.topMargin = margin;
}
param.height = mGridLayout.getHeight()/2;
param.width = mGridLayout.getWidth()/2;
mGridLayout.addView(view, param);
picasso.load(post.url).fit().centerCrop().into(view);
}
}
}
¿Por qué parece que esto se salta en el primer pase de representación para los artículos que no son visibles inicialmente?
EDITAR: Hice un poco de refactorización para poder volver a utilizar las vistas de imagen en lugar de eliminar / crear nuevas y he descubierto que las vistas que se agregan para esos elementos no reciben una devolución de llamada en curso. Todos los demás elementos obtienen la devolución de llamada onLayoutChange, y como antes, los elementos que no reciben la llamada inicialmente, la obtienen después de que me desplazo hacia arriba y me desplazo hacia abajo nuevamente. Ver:
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
view.removeOnLayoutChangeListener(this);
picasso.load(post.url).into(view);
}
});
Edición: 15 de noviembre
Aquí está el xml para el diseño del usuario
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
android:id="@+id/album_card_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
card_view:cardCornerRadius="@dimen/feed_card_corner_radius"
card_view:cardElevation="@dimen/cardview_default_elevation"
card_view:cardMaxElevation="@dimen/cardview_default_elevation"
card_view:cardUseCompatPadding="true"
xmlns:autofit="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.percent.PercentRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/percent_layout">
<GridLayout
android:background="@color/white"
android:id="@+id/preview_grid_layout"
android:columnCount="2"
android:rowCount="2"
android:alignmentMode="alignBounds"
android:layout_height="0dp"
android:layout_width="0dp"
app:layout_widthPercent="100%"
app:layout_aspectRatio="100%">
</GridLayout>
</android.support.percent.PercentRelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/card_padding_4dp"
android:orientation="vertical">
<TextView
android:id="@+id/album_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Family Photos"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Como intenta hacer una lista, quizás deba considerar utilizar la clase RecyclerView . De esta forma, podrá utilizar LayoutManager para diseñar sus imágenes más fácilmente. Esto podría ser mejor ya que su diseño no es una grilla estándar (que tiene columnas de ancho y altura constantes). Un ejemplo interesante a mirar para un LayoutManager personalizado se puede encontrar aquí .
Como el código completo no está allí, entonces puedo adivinar. Parece que cuando se desplaza por primera vez, el adaptador de reciclado crea las vistas antes de que la grilla tenga la oportunidad de crear y agregar vistas. Supongo que si usas otro adaptador, es decir, un adaptador de red para rellenar tus vistas de cuadrícula, debería funcionar bien.
Estoy entusiasmado de que está usando onScrollListener para su vista de reciclador para detectar el desplazamiento y luego cargar más cuadrículas que a su vez cargan más elementos de cuadrícula a través del adaptador de cuadrícula para cada cuadrícula.