I am trying to create a gridView of scrollable images for my android app.Each Grid consist of textView as caption and viewFlipper and each viewflipper is having auto changing images using imageView. I am using the ArrayAdapter with holder pattern to set the view.
Problem Statement:
I am able the get the setup working but with a major problem. The problem is that ViewFlipper of each grid is showing images of other all other grid but the textview i.e. caption is showing correctly. Ideally, ViewFlipper of each grid should show images flipping within its own sets. But it is showing images of other grid's viewflipper images as well.
When i am looking at the logs, I can see getView is being called most of the time with zero position. I am not able to understand its behaviour. As per my understanding i have done everything correctly and each grid should have image flipping among its own sets of images. But its not working as per expectation.
Adapter Code:-
public class GalleryImageAdapter extends ArrayAdapter<GalleryImage> {
public GalleryImageAdapter(Context context, int resource) {
super(context, resource);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if( convertView == null ) {
holder = new ViewHolder();
convertView = LayoutInflater.from(getContext()).inflate( R.layout.view_gallery_thumbnail, parent, false );
holder.vp = (ViewFlipper) convertView.findViewById( R.id.viewFlipper );
holder.caption=(TextView) convertView.findViewById(R.id.textView);
convertView.setTag( holder );
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.caption.setText(getItem(position).getCaption());
Log.e("GalleryAdapter", "Caption-out" + getItem(position).getCaption());
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
for (int i = 1; i <= 4; i++) {
String slideUrl = null;
holder.image1 = new ImageView(getContext());
holder.image1.setScaleType(ImageView.ScaleType.FIT_XY);
holder.image1.setAdjustViewBounds(true);
holder.image1.setLayoutParams(layoutParams);
if (getItem(position).getCaption().equals("AD")){
slideUrl = "http://www.example.com/projects/musicvideos/img/"+ getItem(position).getSlideBg(i) ;
}else {
slideUrl = "http://www.example.com/vi/" + getItem(position).getSlideBg(i) + "/xyz.jpg";
}
Log.e("GalleryAdapter", "slideUrl" + slideUrl);
Picasso.with(getContext()).load(slideUrl)
.placeholder(getContext().getResources().getDrawable(R.drawable.place))
.error(getContext().getResources().getDrawable(R.drawable.place))
.into(holder.image1);
holder.vp.addView(holder.image1);
}
holder.vp.setInAnimation(getContext(), android.R.anim.fade_in);
holder.vp.setOutAnimation(getContext(), android.R.anim.fade_out);
holder.vp.setAutoStart(true);
holder.vp.setFlipInterval(4000);
return convertView;
}
private class ViewHolder {
ImageView image1;
ViewFlipper vp;
TextView caption;
}
}
view_gallery_thumbnail.xml:-
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/textView"
android:textColor="#304f9f"
android:textColorHighlight="#304f9f"
android:textStyle="bold"
android:textSize="15sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/textView"
/>
</RelativeLayout>
I am fetching data dynamically and logs confirms that data is being fetched correctly but adapter is somehow not displaying it in correct order. I am using picasso for image display.
I am stuck in this problem from last 15 days without any success. I have read and tried all suggestion for similar problem mentioned on stackoverflow without any success.
Please help.
I think your issue is that you never remove any views from any of the ViewFlipper
s when a grid item is recycled. What this means is the ViewFlipper
already has children from a previous getView()
call, and now you are adding more child views to it when it's recycled. A simple one-line fix is to remove all its previous children before the for loop:
holder.vp.removeAllViews();
for (int i = 0; i < 4; i++) { ... }
Alternatively, you could put four ImageView
s in your layout XML inside the ViewFlipper
tag and just bind new images to them instead of actually removing, constructing, and adding new views every time it's recycled (and it would also be more efficient). In that case your ViewHolder should hold all four of them and you should not use any addView()
and removeAllViews()
calls. You also would have to call imageView.setImageBitmap(null)
to clear the current image while the proper one is being loaded, so that the user does not see an incorrect image that was bound previously.
EDIT
Put ImageViews in your layout:
<RelativeLayout ... >
<TextView ... />
<ViewFlipper ... >
<!-- HERE -->
<ImageView android:id="@+id/image1" ... />
<ImageView android:id="@+id/image2" ... />
<ImageView android:id="@+id/image3" ... />
<ImageView android:id="@+id/image4" ... />
</ViewFlipper>
</RelativeLayout>
Update the ViewHolder to also hold the ImageViews. You'll have to find them all and assign them when convertView
is null.
class ViewHolder {
ViewFlipper viewFlipper;
TextView textView;
ImageView[] images;
}
Then your binding logic doesn't need to remove or add any views to ViewFlipper, just update the images.
for (int i = 0; i < 4; i++) {
ImageView imageView = holder.images[i];
// This is a recycled view, so clear the previous image, otherwise there
// might be flickering in the UI
imageView.setImageBitmap(null);
// load proper image for this item
String url = ...
Picasso.with(getContext()).load(url)
...
.into(imageView);
}