Sunday, November 22, 2009

Displaying Images from SD Card In Android - Part 2

In one of my previous posts, I wrote about how to fetch and display images from the SD card. The problem with the previous post is that one would have to wait until the first couple of images are available and shown on the screen. This implies that when the user wants to see the images, he will wait a couple of seconds until the first screen of images is available. The code that I'm going to post here works more like the Gallery application, meaning that one image at a time will be displayed on the screen. To achieve this effect, I used an AsyncTask, which fetches one image at a time in the background, and adds that image to the grid view during the progress update.

package blog.android.sdcard2;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemClickListener;

/**
 * Loads images from SD card. 
 * 
 * @author Mihai Fonoage
 *
 */
public class LoadImagesFromSDCardActivity extends Activity implements
OnItemClickListener {
    
    /**
     * Grid view holding the images.
     */
    private GridView sdcardImages;
    /**
     * Image adapter for the grid view.
     */
    private ImageAdapter imageAdapter;
    /**
     * Display used for getting the width of the screen. 
     */
    private Display display;

    /**
     * Creates the content view, sets up the grid, the adapter, and the click listener.
     * 
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        // Request progress bar
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.sdcard);

        display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

        setupViews();
        setProgressBarIndeterminateVisibility(true); 
        loadImages();
    }

    /**
     * Free up bitmap related resources.
     */
    protected void onDestroy() {
        super.onDestroy();
        final GridView grid = sdcardImages;
        final int count = grid.getChildCount();
        ImageView v = null;
        for (int i = 0; i < count; i++) {
            v = (ImageView) grid.getChildAt(i);
            ((BitmapDrawable) v.getDrawable()).setCallback(null);
        }
    }
    /**
     * Setup the grid view.
     */
    private void setupViews() {
        sdcardImages = (GridView) findViewById(R.id.sdcard);
        sdcardImages.setNumColumns(display.getWidth()/95);
        sdcardImages.setClipToPadding(false);
        sdcardImages.setOnItemClickListener(LoadImagesFromSDCardActivity.this);
        imageAdapter = new ImageAdapter(getApplicationContext()); 
        sdcardImages.setAdapter(imageAdapter);
    }
    /**
     * Load images.
     */
    private void loadImages() {
        final Object data = getLastNonConfigurationInstance();
        if (data == null) {
            new LoadImagesFromSDCard().execute();
        } else {
            final LoadedImage[] photos = (LoadedImage[]) data;
            if (photos.length == 0) {
                new LoadImagesFromSDCard().execute();
            }
            for (LoadedImage photo : photos) {
                addImage(photo);
            }
        }
    }
    /**
     * Add image(s) to the grid view adapter.
     * 
     * @param value Array of LoadedImages references
     */
    private void addImage(LoadedImage... value) {
        for (LoadedImage image : value) {
            imageAdapter.addPhoto(image);
            imageAdapter.notifyDataSetChanged();
        }
    }
    
    /**
     * Save bitmap images into a list and return that list. 
     * 
     * @see android.app.Activity#onRetainNonConfigurationInstance()
     */
    @Override
    public Object onRetainNonConfigurationInstance() {
        final GridView grid = sdcardImages;
        final int count = grid.getChildCount();
        final LoadedImage[] list = new LoadedImage[count];

        for (int i = 0; i < count; i++) {
            final ImageView v = (ImageView) grid.getChildAt(i);
            list[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
        }

        return list;
    }
    /**
     * Async task for loading the images from the SD card. 
     * 
     * @author Mihai Fonoage
     *
     */
    class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {
        
        /**
         * Load images from SD Card in the background, and display each image on the screen. 
         *  
         * @see android.os.AsyncTask#doInBackground(Params[])
         */
        @Override
        protected Object doInBackground(Object... params) {
            //setProgressBarIndeterminateVisibility(true); 
            Bitmap bitmap = null;
            Bitmap newBitmap = null;
            Uri uri = null;            

            // Set up an array of the Thumbnail Image ID column we want
            String[] projection = {MediaStore.Images.Thumbnails._ID};
            // Create the cursor pointing to the SDCard
            Cursor cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                    projection, // Which columns to return
                    null,       // Return all rows
                    null,       
                    null); 
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID);
            int size = cursor.getCount();
            // If size is 0, there are no images on the SD Card.
            if (size == 0) {
                //No Images available, post some message to the user
            }
            int imageID = 0;
            for (int i = 0; i < size; i++) {
                cursor.moveToPosition(i);
                imageID = cursor.getInt(columnIndex);
                uri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageID);
                try {
                    bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
                    if (bitmap != null) {
                        newBitmap = Bitmap.createScaledBitmap(bitmap, 70, 70, true);
                        bitmap.recycle();
                        if (newBitmap != null) {
                            publishProgress(new LoadedImage(newBitmap));
                        }
                    }
                } catch (IOException e) {
                    //Error fetching image, try to recover
                }
            }
            cursor.close();
            return null;
        }
        /**
         * Add a new LoadedImage in the images grid.
         *
         * @param value The image.
         */
        @Override
        public void onProgressUpdate(LoadedImage... value) {
            addImage(value);
        }
        /**
         * Set the visibility of the progress bar to false.
         * 
         * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
         */
        @Override
        protected void onPostExecute(Object result) {
            setProgressBarIndeterminateVisibility(false);
        }
    }

    /**
     * Adapter for our image files. 
     * 
     * @author Mihai Fonoage
     *
     */
    class ImageAdapter extends BaseAdapter {

        private Context mContext; 
        private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();

        public ImageAdapter(Context context) { 
            mContext = context; 
        } 

        public void addPhoto(LoadedImage photo) { 
            photos.add(photo); 
        } 

        public int getCount() { 
            return photos.size(); 
        } 

        public Object getItem(int position) { 
            return photos.get(position); 
        } 

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

        public View getView(int position, View convertView, ViewGroup parent) { 
            final ImageView imageView; 
            if (convertView == null) { 
                imageView = new ImageView(mContext); 
            } else { 
                imageView = (ImageView) convertView; 
            } 
            imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
            imageView.setPadding(8, 8, 8, 8);
            imageView.setImageBitmap(photos.get(position).getBitmap());
            return imageView; 
        } 
    }

    /**
     * A LoadedImage contains the Bitmap loaded for the image.
     */
    private static class LoadedImage {
        Bitmap mBitmap;

        LoadedImage(Bitmap bitmap) {
            mBitmap = bitmap;
        }

        public Bitmap getBitmap() {
            return mBitmap;
        }
    }
    /**
     * When an image is clicked, load that image as a puzzle. 
     */
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {        
        int columnIndex = 0;
        String[] projection = {MediaStore.Images.Media.DATA};
        Cursor cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                projection,
                null, 
                null, 
                null);
        if (cursor != null) {
            columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToPosition(position);
            String imagePath = cursor.getString(columnIndex); 

            FileInputStream is = null;
            BufferedInputStream bis = null;
            try {
                is = new FileInputStream(new File(imagePath));
                bis = new BufferedInputStream(is);
                Bitmap bitmap = BitmapFactory.decodeStream(bis);
                Bitmap useThisBitmap = Bitmap.createScaledBitmap(bitmap, parent.getWidth(), parent.getHeight(), true);
                bitmap.recycle();
                //Display bitmap (useThisBitmap)
            } 
            catch (Exception e) {
                //Try to recover
            }
            finally {
                try {
                    if (bis != null) {
                        bis.close();
                    }
                    if (is != null) {
                        is.close();
                    }
                    cursor.close();
                    projection = null;
                } catch (Exception e) {
                }
            }
        }
    }

}

The sdcard.xml file:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <GridView  
        android:id="@+id/sdcard"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp" 
        android:stretchMode="columnWidth"
        android:gravity="center" />
        
</FrameLayout>    

That's it. Let me know if you have any questions.

Enjoy!

84 comments:

Hyperjetta said...

Hi Mihai,

Can you show me an example of how you would obtain the URI for the image that is selected?

Mihai Fonoage said...

You already have the path to that image in the imagePath variable. Just use a URI constructor that takes a strings as its input and you should be all set.

surya's said...

Hi Mihai,
I have written similar program to show the images(but not thumbnails) from sdcard in emulator, it works fine in emulator but on G1 device, it shows the java.lang.OutOfMemoryError can you guess the problem, and above your program does not show anything when I clicked on image even I have written code to show the image in dialog box

Mihai Fonoage said...

Hi surya's,

My program does not display the image you clicked on. You have to use the scaled bitmap I create from the image that was clicked on(referenced by useThisBitmap) and display it in an image view or something similar.

Regarding your problem, most probably the error results from trying to load a to big of an image. Try scaling the image, or, when decoding the stream, use the BitmapFactory.Options class and set the inSampleSize to some power of 2. Do a search on this on Android Forums and you will get many many similar answers that will help.

surya's said...

Mihai,Thank you

喵喵 said...

hello

I would like to ask

why the following codes
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setPadding(8, 8, 8, 8);
imageView.setImageBitmap(photos.get(position).getBitmap());
return imageView;


need to be placed out of the if case block



i tried to move them inside
if (convertView == null) {

}

the outcome is that all the bitmap drawn are the same as the first images of the cursor

Mihai Fonoage said...

The idea here is that you want to reuse the ImageView as much as possible to save memory. Once you have the ImageView (a new one for the first image, or a reused one for the rest of the images), you just need to add the new bitmap to it and return it, which will then be displayed in the grid you previously created.

c group said...

What about reading pictures from a specific folder. e.g Can we read images from "sdcard/myPictures". Thanks for the post. It works fine.

Mihai Fonoage said...

Sure you can, but you would need to use something different than a Cursor, something like creating a file to that directory, using listFiles method to get an array of files from that directory, and then go through each of them, get an input stream, and create your Bitmap.

c group said...

Mihai thank you for your quick response. I have developed application as you pointed out but it crashes sometimes on start, not always. I have used AsyncTask as you wrote, while reading pictures from specific folder. I suppose you have seen such an error. Do you have any idea which event triggers app crash on start. Thank you again.

Mihai Fonoage said...

Actually, I am using the method mentioned and it did not crash on me yet. You have to be more specific on what error you get. Look into LogCat and see what error messages you get. The code that I use is similar to the one below:
File imagesDir = new File(Environment.getExternalStorageDirectory().toString() + "/YOUR_DIRECTORY");
imageList = imagesDir.listFiles();
for (File image : imageList)
try {
bitmap = BitmapFactory.decodeStream(image.toURL().openStream());
//use bitmap and recycle afterwards
} catch (IOException e) {
e.printStackTrace();
}

c group said...
This comment has been removed by the author.
c group said...

Mihai, i found the error.
setProgressBarIndeterminateVisibility(true);

This function execution must be on UI side. Which means you can not call it in doInBackground. I have placed it just before loadImages() and it solved my issues. Now i am able to debug on emulator and run program on horizontal layout.

One more thing is that why you have called grid.getChildCount(); instead of grid.getCount(); at onRetainNonConfigurationInstance. If you flip screen from vertical layout to horizontal layout then some of the pictures will be missed.

Mihai Fonoage said...

You are right, there is no reason not to call getCount instead of getChildCount, since all children views (not only the visible ones) should be "freed". Thanks for pointing that out.

Anonymous said...

Could you please elaborate more on how to display pics from a specific folder on the SDCard? Thanks!

Mihai Fonoage said...

Use something like
File imagesDir = new File(Environment.getExternalStorageDirectory().toString() + "/pathToDirectory");
File[] imageList = imagesDir.listFiles();
for (File imagePath : imageList) {
bitmap = BitmapFactory.decodeStream(imagePath.toURL().openStream());
}

Anonymous said...

How will the list of images (bitmap) be then integrated with the galleryview? I am sorry if this is a newbie question. I was not able to find this information online.

Anonymous said...

To clarify further, I am interested in retrieving the name of the file once the gallery view displays images from a specific folder. Thanks!

Anonymous said...

Please ignore my previous comments. I actually got a version to work based on your previous reply!

Here are my changes:

File imagesDir = new File(Environment.getExternalStorageDirectory().toString() + "/mydir");
imageList = imagesDir.listFiles();

In onItemClick (to get the
selected file path):
-----------------------
String imagePath = imageList[position].getAbsolutePath();

In ImageAdapter:getView:
-----------------------
imageView.setImageBitmap(BitmapFactory.decodeStream(imageList[position].toURL().openStream()) );

Is this the ideal way or am I missing out on any optimizations?

aamir said...

Hi Mihai,

I am using your code imagesload from sdcard ...its working fine
Thanks.....but if i am using in gallery and grid view at a time then generate run time exception outofmemory and Async threadPoolExecutor ...so how can I solve this issue ???can you guide me please ...thanks

RO_manuV said...

Hello Mihai,

My goal is to post on FaceBook an image picked from the sdcard.

To connect to FaceBook I use this code an I try to send the chosen image like in their example, passing as image source the URI of my picture.

Using your code, I get the uri ("content://media/external/images/thumbnails") and the imagePath ("/sdcard/DCIM/.thumbnails/1273763440079.jpg"), which I tried to pass as image source.

I also tried to use Uri.parse("file://"+ imagePath), resulting in "file:///sdcard/DCIM/.thumbnails/1273763440079.jpg".

As a last attempt, I passed even "file:///sdcard/myImage.jpg"

Well, none of the above works and I don't have any idea what I should do.

Please, could you give me a piece of advice?


Thank you a lot, I am very gratefull for any hint.

Sameer said...

Hi Mihai,

Great source code. I need one help on showing html file from SD card. I have a html files stored on SD card and simply want to show on view or Browser. Could you please help me.

Randika said...

hi Mihai,

this works fine, so could please tell me how to load videos as well as audio files

regards,
MIke

sumit saxena said...

very good work ......

i used this code for image viewer and now i try to send selected image to picasa via email so i need to open email activity with selected image as attachment ,i try lot but it not done ,can you help me on that ....i share my code

public void onItemClick(AdapterView parent, View v, int position, long id) {

int columnIndex = 0;
int position =0;
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,null,null, null);
if (cursor != null)
{ columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToPosition(position);
String imagePath = cursor.getString(columnIndex);
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("jpeg/image");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{picasa_id+"."+secret_word+"@picasaweb.com"});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, album_name);
emailIntent.putExtra(android.content.Intent.EXTRA_STREAM,Uri.parse(imagePath));
Toast.makeText(this, "please atteched the image file", 10000).show();
this.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
finish();
}

this code work fine but when i send that mail to picasa a a diffent image is display .......or null image in place of selected image?


thanx ....
sumit

amihud said...

Thank for the code
it's working fine.
When i try to add a function that include
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);

it crash , this function work fine
in other application

do you have ant idea why the cursor crash ?

nitin said...

Hi Mihai,

I am working on an application which require
1. select an image folder
2. display the images in the folder in a GridView.

I am wondering, if you know of any example of Gallery for folder selection?

amihud said...

to use the sdcard you can do it like this change sSDcardLooky
to your folder

protected Object doInBackground(Object... params) {
setProgressBarIndeterminateVisibility(true);
Bitmap bitmap = null;
Bitmap newBitmap = null;
Uri uri = null;
imageList.clear();
if(imageList.size()==0){
boolean exists = (new File(sSDcardLooky)).exists();
if (exists)
{ // File or directory exists }
//Toast.makeText(this, "exist", Toast.LENGTH_SHORT).show();
}
else { // File or directory does not exist }

//Toast.makeText(this, "not exist", Toast.LENGTH_SHORT).show();
}

// Search sdcard/image catalog picture file , As the gallery source ,
File sdcard=new File(sSDcardLooky);
//Log.d(TAG,"Gallery1.sdcard."+sdcard.getName());
File[] imageFiles=sdcard.listFiles(new FileFilter(){

public boolean accept(File arg0){
String fileName=arg0.getName();
String ex=arg0.getName().substring(fileName.lastIndexOf(".")+1,fileName.length()).toLowerCase();
if(ex==null||ex.length()==0){
return false;
}else{
if(ex.equals("jpg")||ex.equals("bmp")||ex.equals("png")||ex.equals("gif")){
return true;
}
return false;
}

}
});


for(int i=0 ; i<imageFiles.length ; i++){
File file=imageFiles[i];
//Log.d(TAG,"Gallery1.file abs Path."+file.getAbsolutePath()+" path."+file.getPath());
// vViewPic(imageFiles[i].getName());
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());

if (bitmap != null) {
newBitmap =
Bitmap.createScaledBitmap(bitmap, 70, 70, true);
bitmap.recycle();
if (newBitmap != null) {
publishProgress(new LoadedImage(newBitmap));
}
}

//imageList.add(BitmapFactory.decodeFile(file.getAbsolutePath()));
//sf = file.getAbsolutePath();
//Toast.makeText(this, sf, Toast.LENGTH_SHORT).show();
//vViewPic("file://"+sf);
}


}
return null;
}

weihua said...

Hi Mihai:

GREAT WORK!!!!!
Your code helped me a lot!

Thank you!!

Tee said...

correct me if I'm wrong but you are caching all images that are displayed in the gridview ? so if there are 500 images in scard you cached those 500 images in the ArrayList, right ?

If that is the case then isn't that problem, the phone will run out of memory ?

Mihai Fonoage said...

Yes I am, but it is a scaled version of the initial image, not the actual image. And if there is no more memory and the Activity gets destroyed, I free up the resources related to each bitmap. In the future, I could probably only cache the images that are currently visible on the screen, do some lazy initialization, and reuse the bitmaps that are no longer visible, something similar to how a UITableViewCell is reused on the iPhone.

Tee said...

Can you write pseudo-code of the idea that you mentioned ?

Thanks

Jon said...

Hey there... I've seen alot of posts on how to utilize your code with a specific directory, but am having a hard time actually implementing the change. Could you help? However, thanks again for the great post. This method is lightning fast!

abhinandkr said...

Hi Mihai,

The code from part 1 worked, although it is not displaying all the images.

In this part 2 code, it is giving an exception while in doInBackground().
Here is the message:
"android.View.viewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."

What can I do to correct it? I don't see any explicit thread creation anywhere.

abhinandkr said...

Hi Mihai,

I corrected my previous problem.
Anyway, I want to enhance this code by making it like the Media Gallery. I want to put a checkmark on the photo, when it is touched. Basically, a Select Image feature. How do I do that?
I created a new GridView above the existing one, which takes in the Bitmap of the selected image, but I unable to refresh the screen. Besides, that can be too costly.
Can you help me, please?

Tom said...

Need remove the line 157 - setProgressBarIndeterminateVisibility(true); - , which is throwing an exception. And then it works really nice!

Mihai Fonoage said...

@Tom and @c group - Thanks for the setProgressBarIndeterminateVisibility(true); issue, I made the suggested changes.

Anonymous said...

Hi Mihai,
I couldn't find any contact information on the blog, I made an app using some part of it and I would like to thank you for sharing it!!!

Francisco said...

Hi Mihai I'm working on an image manipulation framework and was using some other method to bring out thumbnails. I wanted to thank you because the method you're using with AsyncTask is superior and I will incorporate it in the framework.
I'm also using the MediaScannerConnection to solve the issue with thumbnail creation of new images on the SDCARD and it works pretty well. Here's the function I created for that:

private void scanMedia(){
String[] sdFiles=null;
File sdPath = Environment.getExternalStorageDirectory();

File[] sdImageArray = sdPath.listFiles(new FilenameFilter(){
@Override
public boolean accept(File dir, String name)
{
return ((name.endsWith(".jpg"))||(name.endsWith(".png")));
}
});
sdFiles = new String[sdImageArray.length];

for(int i= 0 ; i< sdImageArray.length; i++)
{
sdFiles[i] = sdImageArray[i].getAbsolutePath();
}
MediaScannerConnection.scanFile(this, sdFiles, null, null);
}

It should create an array of paths to all the images and make the MediaScannerConnection generate thumbnails for the new ones that haven't been added, solving the thumbnail generation issue.

Thanks again,
Francisco J. Consuegra

Mihai Fonoage said...

Hi Francisco. I'm glad you decided to continue working on the framework. Thanks for the code also; an alternative would be calling the compress method from the Bitmap class to create a 'thumbnail' image on the SD card.

Girish Gaitonde said...

Hi Sir,

It was wonderful piece of code, both part 1 and part 2. Thanks for sharing.

But i have some different requiremnet.
I have went through couple of forums but everybody are showing to display images from SD card.

My app requires that i click on a link,which has 'n' number of photos,and populate all the photos, in a grid view.


I am stuck , can you pls provide any help.

Anonymous said...

Excellent article. Helped me clarify some issues with a project I am researching.

BTW, check out our blog at : http://appfulcrum.com/?page_id=153

L0rDKadaj said...

You got this code sample on Github or Google Code? I wanna try it out.

Varand said...

I cant see the progress bar.
Is it supposed to be so?

I want to have progress bar until all the images be loaded. because if you click on an image or press back button anytime before all the images become loaded, it throws exception.

thank you,
it was nice article.

Varand said...

I cant see the progress bar.
Is it supposed to be so?

I want to have progress bar until all the images be loaded. because if you click on an image or press back button anytime before all the images become loaded, it throws exception.

thank you,
it was nice article.

Varand said...

I cant see the progress bar.
Is it supposed to be so?

I want to have progress bar until all the images be loaded. because if you click on an image or press back button anytime before all the images become loaded, it throws exception.

thank you,
it was nice article.

Anonymous said...

Awesome Link........ Very helpful.... Thanks a lot for sharing it... :)

Anonymous said...

For some reason I had to add a button at the end of sdcard.xml. Otherwise the call
setContentView(R.layout.sdcard);

would fail.

vinod said...

Hi Mihai,
Thanks for the post. It works fine. When orientation of phone changes during loading of images the application crashes due to

java.lang.RuntimeException: An error occured while executing doInBackground()

Could you Please help me

sheetal said...

Hi,

hey i want to display the image selected from sd card into email body and not as an attachment. i tried lots of code still i am not able to solve the problem....if you hav solution plz help me...
Thank you in anticipation

mehdok said...

hi every boy ,
i am using ListActivity for my VIEW and extends ArrayAdapter to inflate rows,
for each row i need thumbnail, i using some part of MIHAI code but i got the wrong thumbnail
i using below code :

public View getView(int position, View convertView, ViewGroup parent)
{
.
.
.
.
.
Long Imageid = getItemId(position);
Uri imageUri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageId);
try
{
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
if(bitmap != null)
{
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, width, width, true);
bitmap.recycle();
icon = newBitmap;
}
else
icon = null;
}
catch(IOException e)
{

}
}

any help ?

zio said...

great job mihai!

praveen said...

Hi Mihai,

iam very much beginner in android. can u tell me that how to create folders in sdcard.and storing the camera pictures in the folder which i am created.and too i want create the folder it should want to dynamic.

Sakul said...

Hi

I have used this code without following code

File imagesDir = new File(Environment.getExternalStorageDirectory().toString() + "/pathToDirectory");
File[] imageList = imagesDir.listFiles();
for (File imagePath : imageList) {
bitmap = BitmapFactory.decodeStream(imagePath.toURL().openStream());
}

But no images are getting loaded.

Can yo please help me?

Sakul said...

Hi

I have used this code WITH following code

< sorry for the last post..it was by mistake>

I have used BELOW code

File imagesDir = new File(Environment.getExternalStorageDirectory().toString() + "/pathToDirectory");
File[] imageList = imagesDir.listFiles();
for (File imagePath : imageList) {
bitmap = BitmapFactory.decodeStream(imagePath.toURL().openStream());
}

But no images are getting loaded.

Can yo please help me?

Anonymous said...

Hi ,
This code helped me a lot .But I am facing a small problem . I am getting duplicate images while displaying the images but when i checked in SD card only one image is stored . Can you pls help me how to retrieve only original image and remove this duplicacy .
Thank You

lluis said...

The code crashes if you click GO-BACK button meanwhile the images are appearing on the phone

Could you point some hints howto solve this situation ?

Thanks in advance

Khoi Nguyen said...

Hi,

I'm new comer in Android, I copy your code and build but it shows error, and when I debug, it fails in function setupViews(): sdcardImages = (GridView) FindViewById(R.id.sdcard);

Please help me, how can I fixed that. I'm using android API 2.2 and run on Desire.

Thanks
Nguyen

fgomiero said...

Hi Mihai, thanks for your code.
I have a question, I would load many images in async way in a gridview, but for every image I have three TextView related.
How can I load images? Images's uri and text of TextView are retrived from content provider.

Motki said...

Great tutorial.
But I have a problem, when I flip the screen (from horinzontal to vertical por example),
I lost the position of my pictures...
So when I click in one of them, the position return null and the aplication return error..
How Can I store the pictures position ?

This is my function that return null when I flip the screen :
public void onItemClick(AdapterView parent, View v, int position, long id) {
Toast.makeText(AsynImagesSDCARDActivity.this, "" + item.get(Integer.toString(position)), Toast.LENGTH_SHORT).show();
}

motki said...

As c.groud said getChildCount() only show the pictures that are shown in the screen...
I change this for grid.getCount(); but I get an error in this line
final ImageView v = (ImageView) grid.getChildAt(i);
because only is recognized the pictures shown in the srcreen and I obtain null values..
How Can I change this line to obtain all the images ??
Thank you!!

Mike said...

imageAdapter.addPhoto(image);
imageAdapter.notifyDataSetChanged();

I used very similar code as yours for an application I have. It allows you to select an item in a listview. The two lines above get called per image on the main thread. As a result selecting an image is often blocked out until they are done loading. I haven't found a fix to this yet. Any ideas?

Mike said...
This comment has been removed by the author.
manuel said...

Hi Mihai,
first of all, thanks for the tutorials. really helpful. but i don't know if its only me but the code seem to run slow and scrolling is jerky too. i only changed for MediaStore.Images.Media.EXTERNAL_CONTENT_URI since some htc phones with senseUI have an issue with thumbnails and have used bitmap options to reduce size but still very slow. Have any idea on why this could be happening. Once again, Thanks for the tutorials.

Thanuja Samarawickrama said...

hi,i got error after running the code.please help me to fix this

11-24 12:31:30.251: W/dalvikvm(610): threadid=7: thread exiting with uncaught exception (group=0x4001d800)
11-24 12:31:30.351: E/AndroidRuntime(610): FATAL EXCEPTION: AsyncTask #1
11-24 12:31:30.351: E/AndroidRuntime(610): java.lang.RuntimeException: An error occured while executing doInBackground()
11-24 12:31:30.351: E/AndroidRuntime(610): at android.os.AsyncTask$3.done(AsyncTask.java:200)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.lang.Thread.run(Thread.java:1096)
11-24 12:31:30.351: E/AndroidRuntime(610): Caused by: java.lang.NullPointerException
11-24 12:31:30.351: E/AndroidRuntime(610): at MihaiBlog.com.MihaiBlogActivity$LoadImagesFromSDCard.doInBackground(MihaiBlogActivity.java:170)
11-24 12:31:30.351: E/AndroidRuntime(610): at android.os.AsyncTask$2.call(AsyncTask.java:185)
11-24 12:31:30.351: E/AndroidRuntime(610): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
11-24 12:31:30.351: E/AndroidRuntime(610): ... 4 more

vishal said...

hi mihai

i just run your code and get the following error


11-28 12:56:48.571: ERROR/AndroidRuntime(200): Uncaught handler: thread AsyncTask #1 exiting due to uncaught exception
11-28 12:56:48.641: ERROR/AndroidRuntime(200): java.lang.RuntimeException: An error occured while executing doInBackground()
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at android.os.AsyncTask$3.done(AsyncTask.java:200)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.lang.Thread.run(Thread.java:1096)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): Caused by: java.lang.NullPointerException
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at com.cls.load.LoadImagesFromSDCardActivity$LoadImagesFromSDCard.doInBackground(LoadImagesFromSDCardActivity.java:180)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at android.os.AsyncTask$2.call(AsyncTask.java:185)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
11-28 12:56:48.641: ERROR/AndroidRuntime(200): ... 4 more



Error belongs to the line

int columnIndex = cursor .getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID);

vishal said...

Hi mihai
Thanks for the tutorial
i just change the _ID to IMAGE_ID in the query and it run fine.
Here is one thing very strange for me that i have a image of size of 3mb in sdcard. This program did not load that image at all. Is there any kind of memory check before loading the image.

I want to load that big image on the click.

vishal said...
This comment has been removed by the author.
Marius said...

Salutare, mersi mult de idee,mi-ai lamurit foarte bine cateva aspecte cu articolul asta!

Jade Byfield said...
This comment has been removed by the author.
Jade Byfield said...
This comment has been removed by the author.
Jade Byfield said...

Hello Mihai, I've been follow your blog for a while man, nice work. My question is that I'm using some of your code to load some images from the user's phone in a gridview as you did above. But can you give an example of how to use the imagePath member? I need to get the path to the fullsized image in the MediaStore based on that imagePath member or some other method, any guidance on this issue?

Jade Byfield said...
This comment has been removed by the author.
jeven soquita said...

how can i zoom in the images ?

Anonymous said...

This code is really inefficient. You are generating two bitmaps and one is rescaled first in the generation and the second one in the view. Come on! The thumbnail has already been generated by the android system (be it gallery or whatever), just pick it up :

bitmap = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),imageID,MediaStore.Images.Thumbnails.MICRO_KIND,
(BitmapFactory.Options) null);


There are two types of thumbnails available:
MINI_KIND: 512 x 384 thumbnail
MICRO_KIND: 96 x 96 thumbnail

venkat raman said...

hai mihai,
i have one problem please clarify, i need to get the image from sdcard and create animation frame using that image. i post my code please clear the error or post your own logic code. please friend clear my doubt


my java code:


import java.io.File;
import java.util.ArrayList;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Environment;
import android.widget.ImageView;

public class FixActivity extends Activity {
/** Called when the activity is first created. */


ImageView image1;
AnimationDrawable animate;
BitmapDrawable bit1,bit2,bit3,bit4;
ArrayList bitmapArray;
File file;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
file = new File(Environment.getExternalStorageDirectory()+"/Pictures/img");
File imageList[] = file.listFiles();
image1=(ImageView)findViewById(R.id.imageView1);
for(int i=0;i();
bitmapArray.add(b);
}
bit1 =new BitmapDrawable(getResources(), bitmapArray.get(0));
bit2 =new BitmapDrawable(getResources(), bitmapArray.get(1));
bit3 =new BitmapDrawable(getResources(), bitmapArray.get(2));
bit4 =new BitmapDrawable(getResources(), bitmapArray.get(3));
int duration1=300;
animate=new AnimationDrawable();
animate.addFrame(bit1, duration1);
animate.addFrame(bit2, duration1);
animate.addFrame(bit3, duration1);
animate.addFrame(bit4, duration1);
animate.setOneShot(true);
image1.setBackgroundDrawable(animate);
animate.setVisible(true, false);
animate.start();

}
}

Sam Levis said...

Mihai Fonoage, Thanks a lot bro...
I modified your code littele bit. coz its not reading the sd card images when i run this code in Tablet.

String[] projection = {"*"};
Cursor cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI
, projection, null, null, null);

int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID);
int size = cursor.getCount();
Log.d("Cursor size=",""+size);
if (size == 0) {
}

int imageID = 0;
for (int i = 0; i < size; i++) {
cursor.moveToPosition(i);
imageID = cursor.getInt(columnIndex);
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
bitmap = BitmapFactory.decodeFile(path);
if (bitmap != null) {
newBitmap = Bitmap.createScaledBitmap(bitmap, 70, 70, true);
bitmap.recycle();
if (newBitmap != null) {
publishProgress(new LoadedImage(newBitmap));
}
}
}

cursor.close();
return null;
}


HELP:
you are reducing the clarity of the image and putting into the grid. when i click on the image can i view that image in some style with zoom and clarity?

Shunami code said...

Hi! Great tutorial! I now know much more than early =)
but 1 thing already is deprecated: display.getWidth()/95;
And I think you know what now to get width you need to use "DisplayMetrics"
Thank you anyway.

Anirudh goyal said...

Hi, Can you Please tell how can I used lastModified() function in it,

I have to get photos I clicked on a particular date,,

But my code is not working

for (int i = 0; i < size; i++)
{
cursor.moveToPosition(i);
imageID = cursor.getInt(columnIndex);

String imagePath = cursor.getString(columnIndex);
File file = new File(imagePath);
Date lastModDate = new Date(file.lastModified());
System.out.println("Today is " + lastModDate );
Date today= new Date();
int results = today.compareTo(lastModDate);
uri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageID);
if(results==-1)
{
try
{
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
if (bitmap != null)
{
newBitmap = Bitmap.createScaledBitmap(bitmap, 70, 70, true);
bitmap.recycle();
if (newBitmap != null)
{
publishProgress(new LoadedImage(newBitmap));
}
}
}
catch (IOException e)
{
//Error fetching image, try to recover
}
}
}
cursor.close();
return null;
}

Mitch Stinson said...

Hi there,

I would be interested in learning more about how to utilize this process in conjunction with our software for data center infrastructure software.

kaustubh patil said...

Mihai Fonoage,you really did gud job.
I use this in my proj bt i have one problem i use onclicklistner in getview when user click on image it works only after all images get load bt i want it work when we adding images too so please help me thanks in advance

Android App Development said...

This is good job to explain this kind of information through this blog that help me to display images from SD card in Android-part-2.

Kimberly said...

Dear Mr. Mihai,

I did like your post, but no image display, why that?
I put image into sdCard already.

Anonymous said...

Display image in GridView

Carlos Martínez said...

Hi Mihai let me congratulate you for this post that you have done...
I realize something extrange when i test this code in the emulator I click one image and show it in a imageView and it works fine, but when I test the same code in my tablet device it never loads the first image an seems like the id is not well attached to each path because if i select the image id=0 it shows me a diferent image of the sdcard in my imageView
Can you help me about this strange question?