I am having trouble with one of my adapters. I have a selection of images which I display in a grid like the following:
This works well when I have a single screen worth of items and they all show up fine. The problem appears when there are more items to scroll down to. Initially when I scroll down further the items appear blank, the GridLayout
containers are there and the code for displaying the images gets run, but they do not appear on the screen as shown here:
If I scroll back up, then scroll back down again, the images appear exactly where they should be:
It seems like I need to refresh the layout or tell the renderer that something has changed, but nothing I try seems to do anything. I have tried calling requestLayout
and invalidate
but it doesn't seem to solve the issue. I also registered a GlobalOnLayoutListener, but that didn't fare any better.
Here is the relevant code:
This is from my onBindViewHolder
method
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();
}
});
}
This is the method I use to layout the images. It basically lays the images out in an irregular grid, so if there is a long or tall image, that image will attempt to take up a full row or a full column depending on what space is free. The reason i set the param width and height is so picasso doesn't complain about the .fit()
method:
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);
}
}
}
Why does this seem to skip out on the first rendering pass for the items not visible initially?
EDIT: I did a bit of refactoring so I could re-use imageviews instead of removing/creating new ones and have found out that the views being added for those items dont get an onLayoutChange callback. All of the other items get the onLayoutChange callback, and as before, the items that don't get the call initially, get it after I scroll up and scroll down again. See:
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);
}
});
Edit: Nov. 15
Here is the xml for the viewholder layout
<?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>