Android – Show images from Flickr with ImageGridView

Flickr is a wonderful image hosting and video hosting of Yahoo. With a free account, you can get a disk space up to 1 TB for uploading and sharing your photos as well as videos. In this post, I would like to make a small demo showing how we can consume the Flickr’s REST web service and display public images of an album on Android phone. The images could be shown either in thumbnail mode or medium mode. The example app is just a proof of concept, you have to improve it when you want to build a same one in practice because I just don’t care about optimizing resources (memory) in source code. You will also ‘learn’ how to implement a singleton pattern (in entire app) with roboguice. That means, small example, interesting use cases.

1. Prerequisites

As always, before getting started, be sure that you’ve already know what roboguice is and how it works. If not, please read this post first before continuing Android, Dependency Injection (IOC) with roboguice and MVVM (Model-View-ViewModel) pattern.

In this demo, we will query Flickr API REST web service, if you read my posts before, you are familiar with API key (Google, Twitter…). For Flickr of Yahoo, we also need that key for our app.

Follow this URL http://www.flickr.com/services/apps/create/apply –> APPLY FOR A NON-COMMERCIAL KEY –> Enter your app’s name and its description. At the end you’ll get your own private API key for using Flickr REST web service

Flickr API Key

Remember to set INTERNET permission for demo app in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

2. User interface

Our demo app is simple. It contains only 2 activities: a gridview and a viewpager activity. The GridView activity provides a gallery view displaying all thumbnails of images of an album. The ViewPager, on the contrary, works in single mode and shows only one single image in big size format. The user can ‘swipe’ left and right to navigate through albums in ViewPager activity.

The images below illustrates how the 2 activities look like

Gallery view

ImageGridView

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
        >
    <GridView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/gridView" android:numColumns="auto_fit" android:gravity="center"
            android:stretchMode="columnWidth"/>
</LinearLayout>

Single image view

Single image view

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />

</LinearLayout>

3. User interface code

3.1 Gallery view

The gallery view is built on a GridView control of Android SDK. With a grid, we have a layout of table with columns and rows. These columns and rows will split the screen into a two dimesions matrix. In each cell of this matrix, we have just to push/insert a thumbnail to it. When all cells are filled with images, we’ll get a beautiful ImageGridView at the end. So, when initializing GridView, we have to define how many columns we would like to have and spacing between cells so that cells don’t stick with each other.

private void initializeComponents() {
	float spacing = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
			ConstantValues.GRIDVIEW_SPACING, getResources().getDisplayMetrics());
	gridView.setNumColumns(DeviceUtil.getDeviceDimensions(MainActivity.this).x / ConstantValues.GRIDVIEW_COLUMN_WIDTH);
	gridView.setPadding((int) spacing, (int) spacing, (int) spacing, (int) spacing);
	gridView.setVerticalSpacing((int) spacing);
	gridView.setHorizontalSpacing((int) spacing);
}

After initialization is finished, images should be pushed now to GridView. Loading data from Internet is a long time progress and it is a network action. In Android, we are not allowed to let an network action run in UI thread. So, put our code into an async task and let it run in background

public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	initializeComponents();
	new LoadImagesFromFlickrTask().execute();
}

The async task looks like following

class LoadImagesFromFlickrTask extends AsyncTask<String, Integer, List> {
	private ProgressDialog progressDialog;
	private Integer totalCount, currentIndex;

	@Override
	protected void onPreExecute() {
		super.onPreExecute();
		progressDialog = new ProgressDialog(MainActivity.this);
		progressDialog.setMessage("Loading images from Flickr. Please wait...");
		progressDialog.show();
	}

	@Override
	protected void onProgressUpdate(Integer... values) {
		super.onProgressUpdate(values);
		progressDialog.setMessage(String.format("Loading images from Flickr %s/%s. Please wait...", values[0], values[1]));
	}

	@Override
	protected List doInBackground(String... params) {
		Flickr flickr = new Flickr(ConstantValues.FLICKR_API_KEY, ConstantValues.FLICKR_FORMAT);
		List photos = flickr.getPhotoSets().getPhotos(ConstantValues.PHOTOSET_ID);
		List result = new ArrayList();
		totalCount = photos.size();
		currentIndex = 0;
		for (Photo photo : photos) {
			currentIndex++;
			List sizes = flickr.getPhotos().getSizes(photo.getId());
			String thumbnailUrl = sizes.get(0).getSource();
			String mediumUrl = sizes.get(4).getSource();
			InputStream inputStreamThumbnail = null,inputStreamMedium=null;
			try {
				inputStreamThumbnail = new URL(thumbnailUrl).openStream();
				inputStreamMedium = new URL(mediumUrl).openStream();
			} catch (IOException e) {
				e.printStackTrace();
			}
			Bitmap bitmapThumbnail = BitmapFactory.decodeStream(inputStreamThumbnail);
			Bitmap bitmapMedium = BitmapFactory.decodeStream(inputStreamMedium);
			result.add(new ImageInfo(photo.getTitle(),bitmapThumbnail ,bitmapMedium ));
			publishProgress(currentIndex, totalCount);
//                if (currentIndex>3)
//                    break;
		}
		currentAppData.setImageInfos(result);
		return result;
	}

	@Override
	protected void onPostExecute(List s) {
		progressDialog.dismiss();
		imageGridViewAdapter = new ImageGridViewAdapter(MainActivity.this);
		gridView.setAdapter(imageGridViewAdapter);
		super.onPostExecute(s);
	}
}

The async task will load images from Flickr by using a helper class Flickr(). This class will parse result from web service into Java objects and stores them to our singleton class ICurrentAppData for later use, but we can skip it for now. You can learn more about it in source code, let’s focus on UI code first.

As I said in many posts before, whenever we want to customize data source for an Android control, let’s use adapter. It’s never a wrong choice. In this demo, we also use adapter for populating the GridView with images from Flickr. In code listing above, when async task is finished, an adapter will be initialized and set to the grid view

public class ImageGridViewAdapter extends BaseAdapter {

    @Inject
    ICurrentAppData currentAppData;
    private Activity activity;

    public ImageGridViewAdapter(Activity activity)
    {
        RoboGuice.getInjector(activity).injectMembers(this);
        this.activity = activity;

    }

    @Override
    public int getCount() {
        return currentAppData.getImageInfos().size();
    }

    @Override
    public Object getItem(int position) {
        return currentAppData.getImageInfos().get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView result ;
        if (convertView == null)
            result = new ImageView(activity);
        else
            result = (ImageView) convertView;

        result.setScaleType(ImageView.ScaleType.CENTER);
        result.setImageBitmap(currentAppData.getImageInfos().get(position).getThumbnailBitmap());
        result.setOnClickListener(new ImageGridViewCellOnClickListener(position));

        return  result;
    }

    class ImageGridViewCellOnClickListener implements View.OnClickListener {
        private int position;
        public ImageGridViewCellOnClickListener(int position)
        {
            this.position = position;
        }
        @Override
        public void onClick(View v) {
            currentAppData.setCurrentPosition(position);
            Intent intent = new Intent(activity, MediumViewActivity.class);
            activity.startActivity(intent);
        }
    }
}

Maybe you are now surprised that the adapter just simply extends BaseAdapter – a one dimension array, not a two dimension matrix as you may think. We use a GridView control to display the images, as usual, a grid consists of columns and rows, they forms a 2 dimension matrix. However in GridView control of Android, we can define a one dimension adapter for it. The items will be managed in a array, GridView is only responsible for splitting the array at defined position to make a new row in user interface.

When the GridView is loaded, every single cells will be drawn through getView(int position, View convertView, ViewGroup parent) function. Our custom adapter needs to push the correct image for each cell and subscribe to click event so that user can view single image in big format.

The images are taken not directly from Flickr anymore but from our singleton ICurrentAppData class. At this time, ICurrentAppData was already initialized and filled with data. We just make a call to it and get data back, don’t have to call Flickr REST web service anymore. It’s a large performance gain.

3.2 Single image view

From the gallery view (GridView) user can tap on any image to go to ‘full-size’ mode. In ‘full-size’ mode, we also would like to allow user to swipe left/right for navigating. ViewPager control is the best candidate for implementing this feature. In single image mode, we maybe also want to display more information about image such as name, taken date… and one button to go back to gallery mode. All these requirements can be accomplished through custom adapter.

public class MediumViewAdapter extends PagerAdapter {
    @Inject
    ICurrentAppData currentAppData;
    private Activity activity;
    private ImageView imageView;
    private TextView textView;

    public MediumViewAdapter(Activity activity) {
        RoboGuice.getInjector(activity).injectMembers(this);
        this.activity = activity;
    }

    @Override
    public int getCount() {
        return currentAppData.getImageInfos().size();
    }

    @Override
    public boolean isViewFromObject(View view, Object o) {
        return view == (LinearLayout)o;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {

        LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View viewLayout = inflater.inflate(R.layout.layout_medium_view, container, false);

        imageView = (ImageView) viewLayout.findViewById(R.id.imageView);
        imageView.setImageBitmap(currentAppData.getImageInfos().get(position).getMediumBitmap());

        ImageButton buttonBack = (ImageButton) viewLayout.findViewById(R.id.imageButtonBack);
        buttonBack.setOnClickListener(ButtonBackOnClickListener);

        textView = (TextView) viewLayout.findViewById(R.id.textViewName);
        textView.setText(currentAppData.getImageInfos().get(position).getName());

        ((ViewPager) container).addView(viewLayout);
        return viewLayout;
    }

    private View.OnClickListener ButtonBackOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            activity.finish();
        }
    };

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        ((ViewPager)container).removeView((LinearLayout)object);
    }
}

Our custom adapter has to implement the PagerAdapter so that ViewPager can ‘adapt’ the data source from adapter to its view. According to Android’s development documentation, when you implement a PagerAdapter, you must override the following methods at minimum

  • instantiateItem(ViewGroup, int)
  • destroyItem(ViewGroup, int, Object)
  • getCount()
  • isViewFromObject(View, Object)

Again, remember to override and implement these functions if you don’t want to see your app crash without any notifications in LogCat 🙂 . Our custom adapter for ViewPager is pretty simple.

All we have to do is telling ViewPager drawing the page according to our predefined layout (in this demo, one textview, one button and one imageview) and then set the values of component as we want. Thank to the singleton object, the code is really clearly, we don’t have to pass any parameters between the gallery view and single image view. All required statuses are already stored in this central object. We just need to query it for information. The click event of button back is subscribed for closing the current single image mode and going back to gallery view.

4. Singeton pattern with roboguice

As you can see, the code of our demo app is clearly thank to the singleton ICurrentAppData object. We don’t have to define any parameters or relationship between the activities. All of logic statuses are stored and easily got back from ICurrentAppData. This object and its properties are static in entire app no matter where we are. It works like a ‘global’ variable. To make something to be singleton like ICurrentAppData with roboguice, we just simply add an annotation @Singleton at the declaration of its implementation class

Registration

public class IOCModule implements Module {
    @Override
    public void configure(Binder binder) {
        binder.bind(ICurrentAppData.class).to(CurrentAppData.class);
    }
}

Interface

public interface ICurrentAppData {
    List getImageInfos();

    void setImageInfos(List imageInfos);

    int getCurrentPosition();

    void setCurrentPosition(int currentPosition);
}

Implementation

@Singleton
public class CurrentAppData implements ICurrentAppData {
   List imageInfos;
    int currentPosition;
    List mediumBitmaps;

    public int getCurrentPosition() {
        return currentPosition;
    }

    public void setCurrentPosition(int currentPosition) {
        this.currentPosition = currentPosition;
    }

    public Bitmap getMediumImage(int position) {
        return mediumBitmaps.get(position);
    }

    public void setMediumImage(int position, Bitmap bitmap) {
        mediumBitmaps.set(position,bitmap);
    }

    @Override
    public List getImageInfos() {
        return imageInfos;
    }

    @Override
    public void setImageInfos(List imageInfos) {
        this.imageInfos = imageInfos;
        mediumBitmaps = new ArrayList(Collections.nCopies(imageInfos.size(),(Bitmap)null));
    }
}

You are absolutely right if telling me that storing images in this singleton class will causes a lot of memory loss. I completely agree with you. As I said above, this demo is a proof of concept. I just make a simple app to show how we can register a singleton object with roboguice and use it in our app. In practice, we should store only small chunk of data of our app into this singleton one, for example current app statuses, settings,… Believe me, it takes little memory but it will make your code much more beautiful, cleaner and easier for writing unit test.

For using the singleton object where the context is not injected by roboguice, you should inject manually by calling

public ImageGridViewAdapter(Activity activity)
{
	RoboGuice.getInjector(activity).injectMembers(this);	//call this line to get injected manually
	this.activity = activity;
}

We now reach the end of post. I hope through this small app, you’ll understand more about how to make and take advantage of a singleton object in your app. Once again, we do a practice with consuming REST web service and creating custom adapter. The source code can be downloaded from following link

Source code : https://bitbucket.org/hintdesk/android-show-images-from-flickr-with-imagegridview

Remember to use your own private Flickr API key in source code.

5. Updates

5.1 22.06.2014

– A demo video to show how demo apps works
– Update source code link

49 thoughts on “Android – Show images from Flickr with ImageGridView”

  1. I have a gridview that displays video thumbline i want to overlap that thumbline with a transparent play button similar to any videos thumblines in android Gallery

  2. I have a question you are executing Background task on a singel thread what if we have a server and we have to download multiple photos and videos and store them in database so is it a good practice to first get the list of all the photos and videos and then execute Parallel Async tasks to get them parallely.

  3. @admin: Sorry because the user can be very confused between your questions and my answers. I let you use your name as you want but it’s nice if you choose other name such as Your name + Last Name.

    @ASP: Your question is very interesting. I don’t have any example for Parallel Async Tasks right now but I’ll make it later. Your question is now in my To Do List. ;).

  4. thank-you admin this is your code from upload file to asp web api can you tell me how can i download videos and photos in android making http request to web api

    public HttpResponseMessage Get(string fileName)
    {
    HttpResponseMessage result = null;

    DirectoryInfo directoryInfo = new DirectoryInfo(HostingEnvironment.MapPath(“~/App_Data/” + UploadFolder));
    FileInfo foundFileInfo = directoryInfo.GetFiles().Where(x => x.Name == fileName).FirstOrDefault();
    if (foundFileInfo != null)
    {
    FileStream fs = new FileStream(foundFileInfo.FullName, FileMode.Open);

    result = new HttpResponseMessage(HttpStatusCode.OK);
    result.Content = new StreamContent(fs);
    result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(“application/octet-stream”);
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue(“attachment”);
    result.Content.Headers.ContentDisposition.FileName = foundFileInfo.Name;
    }
    else
    {
    result = new HttpResponseMessage(HttpStatusCode.NotFound);
    }

    return result;
    }

  5. @Asp: Maybe I don’t understand your question but you made comment in this post where I already show how we can download and then show photo in Android. The post above shows directly how one can consume binary data (photo) from a web service. So what would you like to ask me then?

  6. i want to ask how to write an asp web service that sends binray data to android this post tells us how to consume it i want to know how to send it from the server side when client requests it

  7. thanks for the tutorial really helpful ..but how can i set wallpaper from the view pager ? like button to set wallpaper or to download it what is the best way to do this ?

  8. @developer: You can extend the layout with one more button to set the current image as wallpaper
    @android: I checked the code and it still works.

  9. Is it possible to display the location of the image? I need the code of it as I am doing android application for my project.

  10. Guys, can anyone tell me how do I import this project into Eclipse or Android-studio (IntelliJ)?
    I am having trouble doing so.
    Eclipse is able to locate the project all right (the finish button also light up fine), but I am unable to click the finish button

    Thanks!

  11. Hi Aditya,
    the project was created by Intellij IDEA. I think it can be easily imported to Android Studio. I’ll check later why you can’t import it into Eclipse.

  12. @Alson: I add your question to my ‘To Do List’. I’ll write a demo about GPS info in Android later but I run of free time. So don’t wait for me, try to search for available tutorial in Internet first.

  13. I’ve tried your code and got an error “unfortunately blabla has stopped” on android emulator. LogCat said “Fatal Exception AsyncTask #1 at doInBackground()…”. How to fix this?

    Thanks

  14. @Evan: Thank you for reporting the error. The reason is Flickr requires SSL connection. We just need to use https instead of http and it works again. I also update the code.

  15. @Paul : I can only say that the current code works (at least at my computer). You can check the code out and try again. If it still don’t work, then you have to find out yourself. I can’t be of much help.

  16. @admin: i need to skip the loading window and load the images to grid immediately one by one, how could it be ?

  17. @Kaitou: I don’t have code for your question right now. But I think it’s not too complicated to implement it. Just skipping loading window, in loading async task, load each image and update directly to grid through updating Adapter.

  18. @Admin
    i would like to know if there is a way to skip the loading window from start.

    Also do you think it is better to use flickr instead of google drive or mega. I mean brandwich limitations.

    I have a small app with ~1000 photos and i want to host them from server. Don`t know what to use so the link won`t be removed.

  19. I have grid view which shows images and on click of one it in gridview which is image I open viewpager but viewpager takes time to open and turns blank screen before again loading. Please help what can be problem

  20. @viren: You GUI is freezing because maybe you’re reading large image from disk. Try to cache large image in memory and load it from memory to speed up loading time. In addition, I can’t say more because I don’t know how your code looks like.

  21. Your code is not working its keep on spinning showing message “Loading images from Flickr.Please Wait” .Even after changing the API KEY.

  22. Solved, just adding /auth- to the url and set mobile application at the Flickr app administration page.

  23. hi admin.
    i used your code in my app,and I write by Android studio.When i ran my app on an emulator,it had an error that : “Attempt to invoke virtual method ‘vn.nks.vnlead.flickrutil.Sizes vn.nks.vnlead.flickrutil.JSONObject.PhotosJSON.getSizes()’ on a null object reference” . I don’t know how to fix it.Can you help me?

  24. @QuynhTran: I’ve just checked again and everything works with my code, even in Android studio. Your error code is a NullReferenceException which happens when you try to access a property of a null object. You can easily find out where problem is by debugging your code.

  25. Hello. I followed the tutorial and used the source code, but “setBaseApplicationInjector” can’t be resolved. I’m using RoboGuice 3.0. Any thoughts?

  26. @admin: Okay, so I brought your sample code in, ran it by itself and it ran fine. I pulled it into my project code and was able to solve the “setBaseApplicationInjector” issue. There seems to be problems with putting dependencies in the build.gradle, but putting the .jar files directly into the libs folder worked. However, I’m now getting an error when trying to run your code within my project:

    1) No implementation for com.asylumlakepreserve.interfaces.ICurrentAppData was bound.
    while locating com.asylumlakepreserve.interfaces.ICurrentAppData for field at com.asylumlakepreserve.MainActivity.currentAppData(Unknown Source)
    while locating com.asylumlakepreserve.MainActivity

  27. @Alex : The error says the ICurrentAppData was not injected with a implementation. Check your configuration of Robojuice and test if ICurrentAppData is really registered.

Leave a Reply

Your email address will not be published. Required fields are marked *