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:

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

    ReplyDelete
  2. 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.

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

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

    ReplyDelete
  5. hello clarke,

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

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

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

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

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

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

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

    ReplyDelete
  11. *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.. :)

    ReplyDelete
  12. 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.

    ReplyDelete
  13. 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..

    ReplyDelete
  14. 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 .. :)

    ReplyDelete
  15. 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

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

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

    ReplyDelete
  18. 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 ?

    ReplyDelete
  19. 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!

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

    ReplyDelete
  21. hello Sean Turtle,

    are u run in background thread?

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

    ReplyDelete
  22. 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)

    ReplyDelete
  23. 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)

    ReplyDelete
  24. 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

    ReplyDelete
  25. 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?

    ReplyDelete
  26. 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.

    ReplyDelete
  27. 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.

    ReplyDelete
  28. 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.

    ReplyDelete
  29. 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

    ReplyDelete
  30. thank you. it helped me a lot..

    ReplyDelete
  31. how to get the image path from grid view.

    ReplyDelete
  32. hey...am getting memory out of error

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

    ReplyDelete
  34. 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

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

    here, files shows me null pointer exception error

    ReplyDelete