4

I am trying to make some kind of image gallery where images are loaded in the background and are dynamically added to a gridView when they have finished loading. The image loading works quite well, but the gridView's scrolling behaviour won't work as expected if the images inside the gridView exceed the screen's height.

For testing purposes I am loading 15 dummy images, aligend in two columns. After all images are loaded it seems that the gridView's height fits its content height (8 images or rows on the left column) according to the scrollBar on the right. But if I try to scroll past the 4th row item to reach the bottom of the view (row 5/6/7/8), the scrollBar indicates that the gridView's height has changed and the bottom of the view is reached. Scrolling past the 4th line is not possible. If I scroll up again, the gridView seems to contain 8 rows again.

same gridView with different content size

Left view: gridView seems to contain 15 images. Right view: gridView suddenly seems to contain only 8 images

I have already tried using different approaches like the ExpandableHeightViewGrid mentioned here, but the scrolling behaviour was the same. I would choose using a gridView having multiple columns of images over a single row (like using a listView) because if I there are more than 15 images to load, scrolling to the bottom would be very annoying.

Here is my code:

photo_gallery.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:orientation="vertical"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

    <!-- This is basically a HorizontalScrollView where i add some buttons -->
    <com.my.HorizontalButtonScrollList
        android:id="@+id/horizontalButtonScrollList"
        android:layout_width="match_parent"
        android:layout_height="50dip">
    </com.my.HorizontalButtonScrollList>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipeRefreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <GridView
            android:id="@+id/gridView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:columnWidth="100dp"
            android:numColumns="2"
            android:verticalSpacing="0dp"
            android:horizontalSpacing="0dp"
            android:stretchMode="columnWidth"
            android:gravity="center"
            android:scrollbars="vertical">
        </GridView>

    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>

PhotoGalleryActivity.java (I simplified the code for better readability)

public class PhotoGalleryActivity extends myBaseView {

    private GridView gridView;
    private PhotoGalleryImageAdapter imageAdapter;
    private PhotoGalleryModel[] photoGalleryModels;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.photo_gallery);
        gridView = (GridView) findViewById(R.id.gridView);

        loadImages();
    }

    void loadImages() {

        photoGalleryModels = PhotoGalleryModel.getFakeData();
        imageAdapter = new PhotoGalleryImageAdapter(this, photoGalleryModels);
        gridView.setAdapter(imageAdapter);
    }
}

PhotoGalleryImageAdapter (also simplified)

public class PhotoGalleryImageAdapter extends BaseAdapter {

    private Context mContext;
    private PhotoGalleryModel[] photoGalleryModels;

    public PhotoGalleryImageAdapter(Context c, PhotoGalleryModel[] models){
        mContext = c;
        photoGalleryModels = models;
    }

    @Override
    public int getCount() { return photoGalleryModels.length; }

    @Override
    public Object getItem(int position) { return null; }

    @Override
    public long getItemId(int position) { return 0; }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        final ImageView imageView = new ImageView(mContext);    
        DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
            @Override
            public void MyHttpCallback_OnSuccess(Object data, String responseString)
            {
                if(data instanceof Bitmap) {
                    imageView.setImageBitmap((Bitmap)data);
                }
            }

            @Override
            public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
            {}
        });

        convertView = imageView;    
        return convertView;
    }
}

I would be really glad if someone could help me out here and help me fix my gridView so that I can scroll through all of the loaded images as intended.

Community
  • 1
  • 1
Petzy Bär
  • 135
  • 3
  • 7

1 Answers1

0

Well, it seems that I've solved the problem myself by ignoring it. After I skipped fixing the gridView because I did not know what to do anymore I implemented caching the images with an LruCache (like shown in the Android developer's training page) to save some memory. And suddenly the gridView's scrolling behaviour was fixed, too.

Here are my changes:

PhotoGalleryImageAdapter (now with caching)

public class PhotoGalleryImageAdapter extends BaseAdapter {

    private Context mContext;
    private PhotoGalleryModel[] photoGalleryModels;
    private LruCache<String, Bitmap> mMemoryCache;

    public PhotoGalleryImageAdapter(Context c, PhotoGalleryModel[] models){
        mContext = c;
        photoGalleryModels = models;

        final int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
        final int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
        protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    @Override
    public int getCount() { return photoGalleryModels.length; }

    @Override
    public Object getItem(int position) { return null; }

    @Override
    public long getItemId(int position) { return 0; }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        final ImageView imageView = new ImageView(mContext);
        final String imageKey = photoGalleryModels[position].thumb_image_url;
        final Bitmap bitmapImage = mMemoryCache.get(imageKey);

        if (bitmapImage != null) {
            imageView.setImageBitmap(bitmapImage);
        }
        else {

            DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
                @Override
                public void MyHttpCallback_OnSuccess(Object data, String responseString) {
                    if (data instanceof Bitmap) {
                        mMemoryCache.put(imageKey, (Bitmap)data);
                        imageView.setImageBitmap((Bitmap)data);
                    }
                }

                @Override
                public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
                {}
            });
        }

        convertView = imageView;
        return convertView;
    }

}

I am happy that the gridView is finally working, but I'm not happy with the fact that it didn't work for me without caching the images. I should have set the imageView's bounds inside the imageAdapter's getView() method before the images were loaded, probably. I will try to fix the gridView without using caching and update my answer if I have found a solution to it in case someone has to face the same problems. Until then, I am glad that I managed to make it work :)

UPDATE:

I finally made it work with and without caching. Here is my updated PhotoGalleryImageAdapter:

public View getView(int position, View convertView, ViewGroup parent) {

    final ImageView imageView;
       // set the imagge's bounds if it is not loaded yet
    if (convertView == null) {
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(imageSize, imageSize));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(0, 0, 0, 0);
    }
    else {
        imageView = (ImageView) convertView;
    }

    final String imageKey = photoGalleryModels[position].thumb_image_url;
    final Bitmap bitmapImage = mMemoryCache.get(imageKey);

    if (bitmapImage != null) {
        imageView.setImageBitmap(bitmapImage);
    }
    else {
        imageView.setImageBitmap(emptyBitmap);
        DownloadImageWithURL(photoGalleryModels[position].thumb_image_url, new MyHttpCallback() {
            @Override
            public void MyHttpCallback_OnSuccess(Object data, String responseString) {
                    if (data instanceof Bitmap) {
                    mMemoryCache.put(imageKey, (Bitmap)data);
                    imageView.setImageBitmap((Bitmap)data);
                }
            }

            @Override
            public void MyHttpCallback_OnError(String responseString, ErrorDataModel error)
            {}
        });
    }
    convertView = imageView;
    return convertView;
}

As expected, I needed to set the images bounds before it was loaded.

Because I changed the gridView's numColumns parameter to 'auto_fit', the image's width/height (100dp + stretchMode columnWidth) is calculated as follows:

int imagesPerRow = screenSize.x / (int)(100 * mContext.getResources().getDisplayMetrics().density);
imageSize = screenSize.x / imagesPerRow;

Before loading the imageView's bitmapImage, I create an empty bitmap image and assign it to the imageView (found the code here):

emptyBitmap = Bitmap.createBitmap(imageSize, imageSize, Bitmap.Config.ARGB_8888);

The gridView now works as expected no matter if the LruCache is used or not. I don't know if it's common practice to anser one's own question but in doing so I thought it could help others who facing a similar problem.

Community
  • 1
  • 1
Petzy Bär
  • 135
  • 3
  • 7