Sunday, July 29, 2012

GridView loading photos from SD Card

Previous exercises described how to implement "Gallery-like HorizontalScrollView" and "Vertical Gallery-like ScrollView". Alternatively, it can be displayed in GridView.

GridView


Add a <GridView> in layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <GridView
        android:id="@+id/gridview"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:columnWidth="90dp"
        android:numColumns="auto_fit"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp"
        android:stretchMode="columnWidth"
        android:gravity="center"/>

</LinearLayout>


Main code:
package com.example.androidgridview;

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

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

    public class ImageAdapter extends BaseAdapter {
     
     private Context mContext;
     ArrayList<String> itemList = new ArrayList<String>();
     
     public ImageAdapter(Context c) {
      mContext = c; 
     }
     
     void add(String path){
      itemList.add(path); 
     }

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

  @Override
  public Object getItem(int arg0) {
   // TODO Auto-generated method stub
   return null;
  }

  @Override
  public long getItemId(int position) {
   // TODO Auto-generated method stub
   return 0;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   ImageView imageView;
         if (convertView == null) {  // if it's not recycled, initialize some attributes
             imageView = new ImageView(mContext);
             imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
             imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
             imageView.setPadding(8, 8, 8, 8);
         } else {
             imageView = (ImageView) convertView;
         }

         Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);

         imageView.setImageBitmap(bm);
         return imageView;
  }
  
  public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
   
   Bitmap bm = null;
   // First decode with inJustDecodeBounds=true to check dimensions
   final BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeFile(path, options);
       
   // Calculate inSampleSize
   options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
       
   // Decode bitmap with inSampleSize set
   options.inJustDecodeBounds = false;
   bm = BitmapFactory.decodeFile(path, options); 
       
   return bm;   
  }
  
  public int calculateInSampleSize(
    
   BitmapFactory.Options options, int reqWidth, int reqHeight) {
   // Raw height and width of image
   final int height = options.outHeight;
   final int width = options.outWidth;
   int inSampleSize = 1;
   
   if (height > reqHeight || width > reqWidth) {
    if (width > height) {
     inSampleSize = Math.round((float)height / (float)reqHeight);    
    } else {
     inSampleSize = Math.round((float)width / (float)reqWidth);    
    }   
   }
   
   return inSampleSize;    
  }

 }
    
    ImageAdapter myImageAdapter;

 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        GridView gridview = (GridView) findViewById(R.id.gridview);
        myImageAdapter = new ImageAdapter(this);
        gridview.setAdapter(myImageAdapter);
        
        String ExternalStorageDirectoryPath = Environment
          .getExternalStorageDirectory()
          .getAbsolutePath();
        
        String targetPath = ExternalStorageDirectoryPath + "/test/";
        
        Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
        File targetDirector = new File(targetPath);
        
        File[] files = targetDirector.listFiles();
        for (File file : files){
         myImageAdapter.add(file.getAbsolutePath());
        } 
    }

}


Download the files.

Next:
- Gallery-like single column GridView
- Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method
- Implement OnItemClickListener for GridView

Suggested:
Scale bitmap Efficiently

37 comments:

Anonymous said...

How to add an activity so when I clicked an image it will display the the full image?

Unknown said...

I too would like to know if you have an activity for onItemClick. I have tried to implement one but I just can't seem to get it to work.

Erik said...

Please read Implement OnItemClickListener for GridView.

Unknown said...

Your Awesome!!! Thank you soooo Much! You don't know how long I have been working on this... I'm such a Noob. :)

clarke said...

do we need to create another java class for "ImageAdapter myImageAdapter;" ?
else can we continue in the same code?

Erik said...

hello clarke,

Depends on you. As shown in this example, you can use inner class inside the activity class.

Anonymous said...

I am getting null pointer exception with this code. How should I verify it??

Erik said...

Please describe with more details. Which one get null pointer exception?

Anonymous said...

where is the path?
how can put my path?
in the project or i need to create a folder to put my images?
thanks

Erik said...

The path is set as /test folder in sd card.

To set your path, modify the code:
targetPath = ExternalStorageDirectoryPath + "/test/";

Anonymous said...

Thanks too much
I've searching this 2 days and only this project helps me thanks :))

Unknown said...

*Please read Implement OnItemClickListener for GridView.

That is just catch the gridview's position.. how i can have the image path when i click some item and show the full path.

for example:
/mnt/sdcard/DCIM/test/myimage.jpg

im newbie.. sry for bad english speaking..

Thank You.. :)

Erik said...

hello Welly Pamungkas,

Have you try the example in Implement OnItemClickListener for GridView? What it shown on Toast is "/storage/sdcard0/...JPG". parent.getItemAtPosition(position) is what you need.

Unknown said...

Thx for your answer, but the parent.getItemAtPosition(position) is empty..
i hope that will give some path like what i say..
/mnt/sdcard/DCIM/test/myimage.jpg

Maybe you know about this problem.. Thank you very much.. ^^
you are very help me..

Unknown said...

sorry my less scrupulous in the
@ Override
public Object getItem (int position) {
/ / TODO Auto-generated method stub
itemList.get return (position);
}

Thank you thank you .. ^ ^


for the second question is about the "orderby" .. can i sort the images by date?
this is the last question .. maybe you know about this ..
sorry for troubling you ..

Thank You very much .. :)

Erik said...

hello Welly Pamungkas,

implement your filecomparator(), and call Arrays.sort(files, filecomparator);

Read Sort directory/file in alphabetical order ignore case and Sort directory/file in order of lastModified time

Anonymous said...

My pictures in the GridView are too big and they overlapping each other...What is my mistake?

Unknown said...

thank you very much. i've used that code and it's good, but the columns dont have space between them... why's that?

Mina Adel said...

I've got a question of my own, if you'd be so kind.
When I point the app to the directory of my hi-res images, creating each new thumbnail horizontal line makes my phone lag behind quite a bit. is it somehow possible to pre-compute the bitmaps, save them to a folder, and then just display the thumbnails in connection to the images ?

Erik said...

hello Mina Adel,

I agree that the performance will degrade if the directory have too much picture, and large size!!!

Sure, you can do so.

That means you have to implement another activity to re-generate all thumbnail in another directory, will it waste memory. And the same problem will also happen on the new activity!

Unknown said...

How would you clear the GridView? I'm having trouble. It keeps throwing ArrayOutOfBounds when I clear the Array List with itemName.clear();

Erik said...

hello Sean Turtle,

are u run in background thread?

Please read Load images to GridView from SD Card in background.

Dmitry said...

Hello, when i try run your example i have next error's:

11-27 17:12:33.450: E/AndroidRuntime(14751): FATAL EXCEPTION: main
11-27 17:12:33.450: E/AndroidRuntime(14751): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.androidgridview/com.example.androidgridview.MainActivity}: java.lang.NullPointerException
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1968)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1993)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread.access$600(ActivityThread.java:127)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1159)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.os.Handler.dispatchMessage(Handler.java:99)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.os.Looper.loop(Looper.java:137)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread.main(ActivityThread.java:4507)
11-27 17:12:33.450: E/AndroidRuntime(14751): at java.lang.reflect.Method.invokeNative(Native Method)
11-27 17:12:33.450: E/AndroidRuntime(14751): at java.lang.reflect.Method.invoke(Method.java:511)
11-27 17:12:33.450: E/AndroidRuntime(14751): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
11-27 17:12:33.450: E/AndroidRuntime(14751): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
11-27 17:12:33.450: E/AndroidRuntime(14751): at dalvik.system.NativeStart.main(Native Method)
11-27 17:12:33.450: E/AndroidRuntime(14751): Caused by: java.lang.NullPointerException
11-27 17:12:33.450: E/AndroidRuntime(14751): at com.example.androidgridview.MainActivity.onCreate(MainActivity.java:129)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.Activity.performCreate(Activity.java:4465)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1052)
11-27 17:12:33.450: E/AndroidRuntime(14751): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1932)

Unknown said...

I try to run this code on AVD but each time it crushes. Do you have any idea why it can be or how I can determine the problem?
(There is no errors in the code itself)

Erik said...

hello Pavel Rastopchin,

I'm not sure. May be you need permission of READ_EXTERNAL_STORAGE. http://developer.android.com/reference/android/Manifest.permission.html#READ_EXTERNAL_STORAGE

Unknown said...

I added this permission but still it doesn't work. I've got an error in LogCat:
"called unimplemented opengl es api"

In addition I tried to download the whole project but I saw that the project uses Google API's which useless in this specific App and my AVD can't run it.

I think the problem is that somewhere in code you use OpenGL API and because of it I cant run it on my AVD. But I can't find where you used it?

Unknown said...

After Many retries it works!
Thank you very much. I think this is the BEST example in internet of creating a gallery view from specific folder.

Rob said...

Hi Eric,

First of all, thank you very much for this code. It really helped me a lot—I was finally able to understand the whole procedure.

I have one question though. If I add an image to the directory it's taking the images from while still running the app, the gridview won't update with the new image. I believe I have to use myImageAdapter.notifyDataSetChanged() somewhere, and I tried to right at the end of the function which adds the new image, but it didn't fix the issue.

Thanks again if you can help me with this.

Anonymous said...

Hello Eric,
Thank you very much.
I've tried maybe ten examples before.
Some of them don't work at all.
Some of them work only on the device, but don't work on emulator (!).
Your example works okay on emulator.
Not all understandable yet.
But I will try to arrange it to my program.
Thanks again!!
Niaz.

Anonymous said...

There is a confusing thing in code.
I had crash on first run too.
We have to change this line:
String targetPath = ExternalStorageDirectoryPath + "/";
(It was "/test" before)
Thanks again!
Niaz

Harsh said...

thank you. it helped me a lot..

Unknown said...

how to get the image path from grid view.

Unknown said...

i am getting error

Unknown said...

hey...am getting memory out of error

Erik said...

please read Scale bitmap Efficiently:
http://android-er.blogspot.com/2012/07/scale-bitmap-efficiently.html

Unknown said...

Sir i want to get All images from gallery not target folder as you do in a code.so how i get all images from gallery

Unknown said...

for (File file : files) {
myImageAdapter.add(file.getAbsolutePath());
}

here, files shows me null pointer exception error