Lazy download images into gridView Lazy download images into gridView json json

Lazy download images into gridView


This is how I fetch multiple photos in my activity. You can use parts of it for fit your logic. I use this to fetch Facebook Images from an Album. So my needs are (I am assuming) different from your needs. But again, the logic may be of use to you.

Note: This will be lengthy. ;-)

These are the global declarations for use through the ACtivity:

// HOLD THE URL TO MAKE THE API CALL TOprivate String URL;// STORE THE PAGING URLprivate String pagingURL;// FLAG FOR CURRENT PAGEint current_page = 1;// BOOLEAN TO CHECK IF NEW FEEDS ARE LOADINGBoolean loadingMore = true;Boolean stopLoadingData = false;

This is the code block that fetches the initial set of Images:

private class getPhotosData extends AsyncTask<Void, Void, Void> {    @Override    protected Void doInBackground(Void... arg0) {        // CHANGE THE LOADING MORE STATUS TO PREVENT DUPLICATE CALLS FOR        // MORE DATA WHILE LOADING A BATCH        loadingMore = true;        // SET THE INITIAL URL TO GET THE FIRST LOT OF ALBUMS        URL = "https://graph.facebook.com/" + initialAlbumID                + "/photos&access_token="                + Utility.mFacebook.getAccessToken() + "?limit=10";        try {            HttpClient hc = new DefaultHttpClient();            HttpGet get = new HttpGet(URL);            HttpResponse rp = hc.execute(get);            if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {                String queryAlbums = EntityUtils.toString(rp.getEntity());                JSONObject JOTemp = new JSONObject(queryAlbums);                JSONArray JAPhotos = JOTemp.getJSONArray("data");                // IN MY CODE, I GET THE NEXT PAGE LINK HERE                getPhotos photos;                for (int i = 0; i < JAPhotos.length(); i++) {                    JSONObject JOPhotos = JAPhotos.getJSONObject(i);                    // Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString());                    if (JOPhotos.has("link")) {                        photos = new getPhotos();                        // GET THE ALBUM ID                        if (JOPhotos.has("id")) {                            photos.setPhotoID(JOPhotos.getString("id"));                        } else {                            photos.setPhotoID(null);                        }                        // GET THE ALBUM NAME                        if (JOPhotos.has("name")) {                            photos.setPhotoName(JOPhotos.getString("name"));                        } else {                            photos.setPhotoName(null);                        }                        // GET THE ALBUM COVER PHOTO                        if (JOPhotos.has("picture")) {                            photos.setPhotoPicture(JOPhotos                                    .getString("picture"));                        } else {                            photos.setPhotoPicture(null);                        }                        // GET THE PHOTO'S SOURCE                        if (JOPhotos.has("source")) {                            photos.setPhotoSource(JOPhotos                                    .getString("source"));                        } else {                            photos.setPhotoSource(null);                        }                        arrPhotos.add(photos);                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    @Override    protected void onPostExecute(Void result) {        // SET THE ADAPTER TO THE GRIDVIEW        gridOfPhotos.setAdapter(adapter);        // CHANGE THE LOADING MORE STATUS        loadingMore = false;    }}

This is to detect when the user has scrolled to the end and fetch new set of images:

// ONSCROLLLISTENERgridOfPhotos.setOnScrollListener(new OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        int lastInScreen = firstVisibleItem + visibleItemCount;        if ((lastInScreen == totalItemCount) && !(loadingMore)) {            if (stopLoadingData == false) {                // FETCH THE NEXT BATCH OF FEEDS                new loadMorePhotos().execute();            }        }    }});

And finally, this is how I fetch the next set of images:

private class loadMorePhotos extends AsyncTask<Void, Void, Void> {    @Override    protected Void doInBackground(Void... arg0) {        // SET LOADING MORE "TRUE"        loadingMore = true;        // INCREMENT CURRENT PAGE        current_page += 1;        // Next page request        URL = pagingURL;        try {            HttpClient hc = new DefaultHttpClient();            HttpGet get = new HttpGet(URL);            HttpResponse rp = hc.execute(get);            if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {                String queryAlbums = EntityUtils.toString(rp.getEntity());                // Log.e("PAGED RESULT", queryAlbums);                JSONObject JOTemp = new JSONObject(queryAlbums);                JSONArray JAPhotos = JOTemp.getJSONArray("data");                // IN MY CODE, I GET THE NEXT PAGE LINK HERE                getPhotos photos;                for (int i = 0; i < JAPhotos.length(); i++) {                    JSONObject JOPhotos = JAPhotos.getJSONObject(i);                    // Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString());                    if (JOPhotos.has("link")) {                        photos = new getPhotos();                        // GET THE ALBUM ID                        if (JOPhotos.has("id")) {                            photos.setPhotoID(JOPhotos.getString("id"));                        } else {                            photos.setPhotoID(null);                        }                        // GET THE ALBUM NAME                        if (JOPhotos.has("name")) {                            photos.setPhotoName(JOPhotos.getString("name"));                        } else {                            photos.setPhotoName(null);                        }                        // GET THE ALBUM COVER PHOTO                        if (JOPhotos.has("picture")) {                            photos.setPhotoPicture(JOPhotos                                    .getString("picture"));                        } else {                            photos.setPhotoPicture(null);                        }                        // GET THE ALBUM'S PHOTO COUNT                        if (JOPhotos.has("source")) {                            photos.setPhotoSource(JOPhotos                                    .getString("source"));                        } else {                            photos.setPhotoSource(null);                        }                        arrPhotos.add(photos);                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    @Override    protected void onPostExecute(Void result) {        // get listview current position - used to maintain scroll position        int currentPosition = gridOfPhotos.getFirstVisiblePosition();        // APPEND NEW DATA TO THE ARRAYLIST AND SET THE ADAPTER TO THE        // LISTVIEW        adapter = new PhotosAdapter(Photos.this, arrPhotos);        gridOfPhotos.setAdapter(adapter);        // Setting new scroll position        gridOfPhotos.setSelection(currentPosition + 1);        // SET LOADINGMORE "FALSE" AFTER ADDING NEW FEEDS TO THE EXISTING        // LIST        loadingMore = false;    }}

And this is the helper class to SET and GET the data collected from the queries above:

public class getPhotos {    String PhotoID;    String PhotoName;    String PhotoPicture;    String PhotoSource;    // SET THE PHOTO ID    public void setPhotoID(String PhotoID)  {        this.PhotoID = PhotoID;    }    // GET THE PHOTO ID    public String getPhotoID()  {        return PhotoID;    }    // SET THE PHOTO NAME    public void setPhotoName(String PhotoName)  {        this.PhotoName = PhotoName;    }    // GET THE PHOTO NAME    public String getPhotoName()    {        return PhotoName;    }    // SET THE PHOTO PICTURE    public void setPhotoPicture(String PhotoPicture)    {        this.PhotoPicture = PhotoPicture;    }    // GET THE PHOTO PICTURE    public String getPhotoPicture() {        return PhotoPicture;    }    // SET THE PHOTO SOURCE    public void setPhotoSource(String PhotoSource)  {        this.PhotoSource = PhotoSource;    }    // GET THE PHOTO SOURCE    public String getPhotoSource()  {        return PhotoSource;    }}

If you also want the adapter code, let me know. I use Fedor's Lazy Loading method in the adapter.

Phew. Hope any of this helps. If you have further question, feel free to ask. :-)

EDIT: Adapter code added:

public class PhotosAdapter extends BaseAdapter {    private Activity activity;    ArrayList<getPhotos> arrayPhotos;    private static LayoutInflater inflater = null;    ImageLoader imageLoader;     public PhotosAdapter(Activity a, ArrayList<getPhotos> arrPhotos) {        activity = a;        arrayPhotos = arrPhotos;        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        imageLoader = new ImageLoader(activity.getApplicationContext());    }    public int getCount() {        return arrayPhotos.size();    }    public Object getItem(int position) {        return arrayPhotos.get(position);    }    public long getItemId(int position) {        return position;    }    public View getView(final int position, View convertView, ViewGroup parent) {        ViewHolder holder;        View vi = convertView;        if(convertView == null) {            vi = inflater.inflate(R.layout.photos_item, null);            holder = new ViewHolder();            holder.imgPhoto = (ImageView)vi.findViewById(R.id.grid_item_image);            vi.setTag(holder);          } else {            holder = (ViewHolder) vi.getTag();        }        if (arrayPhotos.get(position).getPhotoPicture() != null){            imageLoader.DisplayImage(arrayPhotos.get(position).getPhotoPicture(), holder.imgPhoto);        }        return vi;    }    static class ViewHolder {        ImageView imgPhoto;    }}

EDIT: Added steps to show Progress while loading:

Add a ProgressBar to you XML where you have the GridView right below it. Play around with the weight if it causes any problems.

<LinearLayout    android:id="@+id/linlaProgressBar"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:gravity="center"    android:orientation="horizontal" >    <ProgressBar        style="@style/Spinner"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:padding="2dp" /></LinearLayout>

In your Java, declare the Linearlayout linlaProgressBar as Global and cast it in the onCreate() and set it's visibility as linlaProgressBar.setVisibility(View.GONE);

And in the onPreExecute() use it like this:

@Overrideprotected void onPreExecute() {    // SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS    linlaProgressBar.setVisibility(View.VISIBLE);}

And finally add, this in the onPostExecute()

// HIDE THE BOTTOM PROGRESS BAR (SPINNER) AFTER LOADING MORE ALBUMSlinlaProgressBar.setVisibility(View.GONE);


You can, take a look to Using adapter Views and GridView from Android Documentation.The most important thing is that the adapter call the method getView passing only the position of the entries showing on screen, and asking for different positions when user scrolls.

The easy way to do is download the required image on the getView method of your adapter with and AsyncTask.

There is an example


Talking from experience, it's tricky to achieve smooth scrolling (and overall responsiveness) while consuming memory reasonably.

It would be a good idea to look for existing solutions first, e.g., start here:Lazy load of images in ListView

We ended up with a custom proprietary solution. It is a background thread that queues download requests and downloads and caches on the external storage only the images that are still visible. When a new image arrives, the view gets notified and decides when to notify the adapter to update.

It also saves the bandwidth, which was important in some cases.