I'm attempting to create a gridview that is loaded with images from a specific folder that resides on an SDCard. The path to the folder is known, ("/sdcard/pictures") , but in the examples I've seen online I am unsure how or where to specify the path to the pictures folder I want to load images from. I have read through dozens of tutorials, even the HelloGridView tutorial at developer.android.com but those tutorials do not teach me what i am seeking.
Every tutorial I have read so far has either:
A) called the images as a Drawable from the /res folder and put them into an array to be loaded, not using the SDCard at all.
B) Accessed all pictures on the SDCard using the MediaStore but not specifying how to set the path to the folder I want to display images form
or
C) Suggested using BitmapFactory, which I haven't the slightest clue how to use.
If I'm going about this in the wrong way, please let me know and direct me toward the proper method to do what I'm trying to do.
OK, after many iterations of trying, I finally have an example that works and I thought I'd share it. My example queries the images MediaStore, then obtains the thumbnail for each image to display in a view. I am loading my images into a Gallery object, but that is not a requirement for this code to work:
Make sure you have a Cursor and int for the column index defined at the class level so that the Gallery's ImageAdapter has access to them:
private Cursor cursor;
private int columnIndex;
First, obtain a cursor of image IDs located in the folder:
Gallery g = (Gallery) findViewById(R.id.gallery);
// request only the image ID to be returned
String[] projection = {MediaStore.Images.Media._ID};
// Create the cursor pointing to the SDCard
cursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
MediaStore.Images.Media.DATA + " like ? ",
new String[] {"%myimagesfolder%"},
null);
// Get the column index of the image ID
columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
g.setAdapter(new ImageAdapter(this));
Then, in the ImageAdapter for the Gallery, obtain the thumbnail to display:
public View getView(int position, View convertView, ViewGroup parent) {
ImageView i = new ImageView(context);
// Move cursor to current position
cursor.moveToPosition(position);
// Get the current value for the requested column
int imageID = cursor.getInt(columnIndex);
// obtain the image URI
Uri uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(imageID) );
String url = uri.toString();
// Set the content of the image based on the image URI
int originalImageId = Integer.parseInt(url.substring(url.lastIndexOf("/") + 1, url.length()));
Bitmap b = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
originalImageId, MediaStore.Images.Thumbnails.MINI_KIND, null);
i.setImageBitmap(b);
i.setLayoutParams(new Gallery.LayoutParams(150, 100));
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setBackgroundResource(mGalleryItemBackground);
return i;
}
I guess the most important section of this code is the managedQuery that demonstrates how to use MediaStore queries to filter a list of image files in a specific folder.
You need to do a few more steps than the GridView tutorial on developer.android.com. Using the following tutorial
http://developer.android.com/resources/tutorials/views/hello-gridview.html
You'll want to add a method to create ImageView's of the files from your sd card:
Create/add a Vector to your class variables (to hold a list of ImageViews):
private Vector<ImageView> mySDCardImages;
Initialize the vector:
mySDCardImages = new Vector<ImageView>();
Create a method to load images:
List<Integer> drawablesId = new ArrayList<Integer>();
int picIndex=12345;
File sdDir = new File("/sdcard/pictures");
File[] sdDirFiles = sdDir.listFiles();
for(File singleFile : sdDirFiles)
{
ImageView myImageView = new ImageView(context);
myImageView.setImageDrawable(Drawable.createFromPath(singleFile.getAbsolutePath());
myImageView.setId(picIndex);
picIndex++;
drawablesId.add(myImageView.getId());
mySDCardImages.add(myImageView);
}
mThumbIds = (Integer[])drawablesId.toArray(new Integer[0]);
Then down in your ImageAdapter method, change
imageView.setImageResource(mThumbIds[position]);
to
imageView.setImageDrawable(mySDCardImages.get(position).getDrawable());
Remove from the ImageAdapter the initialization of mThumbIds. (it should be up with the definition of mySDCardImages. Accessible to both class methods.)
(Quick and dirty version) Make sure to test your path, etc and catch any Exceptions.
In your case BitmaFactory might be a good way to go. Example:
File dir = new File( "/sdcard/pictures" );
String[] fileNames = dir.list(new FilenameFilter() {
boolean accept (File dir, String name) {
if (new File(dir,name).isDirectory())
return false;
return name.toLowerCase().endsWith(".png");
}
});
for(string bitmapFileName : fileNames) {
Bitmap bmp = BitmapFactory.decodeFile(dir.getPath() + "/" + bitmapFileName);
// do something with bitmap
}
Not time to test this but should work ;-)
read this link: http://androidsamples.blogspot.com/2009/06/how-to-display-thumbnails-of-images.html
it shows how to use both mediastore and bitmapfactory.
the way you should chose depends on what exactly you need. if you have a static set of images, it's much better idea to put them to drawables, i think, cause this way it's faster and you don't rely on sd card, which can be removed, corrupt or files could be renamed/deleted
if images are dynamic, then use mediastore or bitmap factory. but keep in mind that putting images into array or something it's quite memory consuming, so you can end up having outofmemory exception
Looks like you want custom gallerry, it will take much time for you,
I suggest you get Custom Camera Gallery for your working.
You will get Photos/Videos in Grid View as you want.
for Kotlin code see the answer of this question
the idea is alslo applicable for java (but you need to modify the code)
Related
How to get multiple images to show up in this coverflow. I am getting null pointer exception and other problems. How to be able to load the images from the SD card, not knowing how many images are on the SD card? The number of images is not fixed. I was able to get one image to show up with no problems by using this code:
i.setImageBitmap(BitmapFactory.decodeFile("/mnt/sdcard/pic03.png"));
it loaded one image, the one specified from the SD card and made all the coverflow images as that one same image, pic03.png, about a dozen of the same one image filled the cover flow. that is great, however i wanted to load all of the images on the SD card. so each image fills each part of the coverflow, not one same image filling all of them.
totally lost here and tried to do this and got null pointer exception from the Logcat and wonder why? does anyone know how to get this to work?
here below is the code that i am working with to give you some idea:
notice that it does work when loading one image from a specified address on the SD card. at least that part worked great, but multiple images?
// added this code inside the onCreate method to load the cursor with images only if the IS_PRIVATE column
// in the sqlite database is equal to the integer 1
String[] img = { MediaStore.Images.Media._ID };
imagecursor = managedQuery(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img,
MediaStore.Images.Media.IS_PRIVATE + "='" + 1 +"'",null, MediaStore.Images.Media._ID + "");
image_column_index = imagecursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
count = imagecursor.getCount();
//-------------------------------------------------------------------
// ImageAdapter class is a nested class inside of the CoverFlowExample class
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private FileInputStream fis;
// originally earlier version of this app loaded images from the R.drawable folder like this
private Integer[] mImageIds = {
// R.drawable.pic01,
// R.drawable.pic02,
// R.drawable.pic03,
// R.drawable.pic04,
// R.drawable.pic05,
// R.drawable.pic06,
// R.drawable.pic07,
// R.drawable.pic08,
// R.drawable.pic09
};
//---------------------------------------------------------------------
// getView() method that is in the ImageAdapter class that extends the BaseAdapter class
// this class is a nested class inside the CoverFlowExample class that extends Activity.
// the CoverFlowExample class is used to implement the coverflow part of the app as an Activity
public View getView(int position, View convertView, ViewGroup parent) {
// to load from resources like SD card
ImageView i = new ImageView(mContext);
// use for single image --> i.setImageBitmap(BitmapFactory.decodeFile("/mnt/sdcard/pic03.png"));
image_column_index = imagecursor.getColumnIndexOrThrowMediaStore.Images.Media.DATA);
imagecursor.moveToPosition(position);
int id = imagecursor.getInt(image_column_index);
i.setImageURIUri.withAppendedPathMediaStore .Images.Media.EXTERNAL_CONTENT_URI, ""+ id));
// image from R.drawable use --> i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(130, 130));
i.setScaleType(ImageView.ScaleType.MATRIX);
return i;
// return mImages[position]; <-- not using this as im am not getting image from R.drawable
}
The problem seems that String[] img = { MediaStore.Images.Media._ID }
this is the only column contained in the imagecursor, while you try to get the DATA column ,imagecursor.getColumnIndexOrThrowMediaStore.Images.Media.DATA)
i am reading the string of MediaStore.Images.Media.DATA from an ArrayList into an array so i can use the images from my SD card instead of the images in the R.drawable folder for a coverflow gallery app. and have run into a brick wall. with the problem of reading integer type vs. string type for image information.
was able to get the URIs from all the images from the SD card into the arraylist and don't know what is the next step to take.
is it worth continuing with this approach or is it better to abandon this and try another way to accomplish the task of changing the source of images for this coverflow app from the R.drawables folder to the SD card.
here is what i did
first, here is the way it gets the images from the R.drawable folder in android using an array as part of the process:
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private FileInputStream fis;
// private Integer[] mImageIds = {
// R.drawable.pic01,
// R.drawable.pic02,
// R.drawable.pic03,
// R.drawable.pic04,
// R.drawable.pic05,
// R.drawable.pic06,
// R.drawable.pic07,
// R.drawable.pic08,
// R.drawable.pic09
// };
private ImageView[] mImages;
my idea was to use a cursor to read the URIs like this "//mnt/sdcard/pic01.png" from the SD card and use these addresses instead of the ones in the array from the R.drawables like this "R.drawable.pic02" in the array.
this is what i did and this part worked with no problems:
i got all the URIs from the images on the SD card and stored them in an arrayList, so far everything works great. i verified that it works perfectly by using a Toast to show the loaded URIs on the activity screen.
ArrayList<String> list1 = new ArrayList<String>();
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cur = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String pic = cur.getString(cur.getColumnIndex(MediaStore.Images.Media.DATA));
list1.add(pic);
// Toast.makeText(CoverFlowExample.this, "data name: " + pic, Toast.LENGTH_SHORT).show();
// Log.d("TAG", "ID: " + pic);
}
}
cur.close();
next i tried to read these strings into the array used for the R.drawables array and this is where i got into trouble: the program uses Integer as the type for the array. so i guess this array is the wrong place to read URIs into. then if this is the wrong place. what to do now?
Integer[] mImageIds = new Integer[list1.size()];
for(int t = 0; t<= list1.size(); t++){
mImageIds[t]= list1.get(t); // type mismatch cannot convert string to integer
}
Toast.makeText(CoverFlowExample.this, "data name: " + y , Toast.LENGTH_SHORT).show();
the error i am getting is "type mismatch, cannot convert string to integer" for this part:
mImageIds[t]= list1.get(t);
here is the rest of the code from the getview part of the app: i am now trying to figure out what to do next.
public View getView(int position, View convertView, ViewGroup parent) {
//Use this code if you want to load from resources from external source
ImageView i = new ImageView(mContext);
// this method is what someone suggested to use but i don't know how to use this, what do i put for "image" and how to read URIs from arrayList to this?
i.setImageBitmap(BitmapFactory.decodeFile("image_" + position));
// this works for reading in only one image from the SD card into the coverflow app
i.setImageBitmap(BitmapFactory.decodeFile("/mnt/sdcard/pic04.png"));
// when getting from R.drawable images --> i.setImageResource(mImageIds[position]);
i.setLayoutParams(new CoverFlow.LayoutParams(130, 130));
i.setScaleType(ImageView.ScaleType.MATRIX);
return i;
//return mImages[position];
}
As you have the URIs / file system path already, you can use one of the createFrom* methods of Drawable java doc in android docu to have it loaded.
I had links for images in hashset. From that, i downloaded pics.
After I create bitmap from that url, I scan file to put it in gallery so that I can see it in gallery.
I use following code.
MediaScannerConnection.scanFile(obContext, new String[] { f.getAbsolutePath() }, null, null);
To see only those particular images in my custom gallery I use folllowing code
Cursor imagecursor=managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
columns,
MediaStore.Images.Media.DATA + " like ? ",
new String[] {"%myDesiredDirectory%"},
null);
So it is scanning myDesiredDirectory before showing me gallery. And after scanning, gallery has my images.
But now I have 2 problems:
While scanning, it is creating thumbnails for newly downloaded images. These thumbnails also visible in gallery.
So same pic is visible twice. Which is not desired.
in next iteration of my app run, i want to show only new set of images. To achieve that i decided to delete images downloaded from first iteration.
I am using following code to delete all files in that dir
if(dirChk==true){
File dir = new File(Path);
String[] children = dir.list();
String[] pathsOfDeletedThumbs=new String[children.length];
Log.d(LOGGER, "dir contains "+children.length);
for (int i = 0; i < children.length; i++) {
String temp = Path+children[i];
Log.d(LOGGER, "temp is "+temp);
new File(dir, children[i]).delete();
}
but still those automatically created thumbnails are present in gallery.
Can anybody please help me to avoid creating thumbnails, and if its not avoidable, can we delete thumbnails also when we delete original files??
Thanks in advance...
I have make on demo that list all contact from contact Uri in that I have made custom list view after adding of contact image my listview scrolling is very slow, following is my code.
public Bitmap getProfilepicture(Activity activity, String address)
{
Bitmap bitmap;
Uri personUri = Uri
.withAppendedPath(Phones.CONTENT_FILTER_URL, address);
Cursor phoneCursor = activity.getContentResolver().query(personUri,
PHONE_PROJECTION, null, null, null);
if (phoneCursor.moveToFirst()) {
int indexPersonId = phoneCursor.getColumnIndex(Phones.PERSON_ID);
long personId = phoneCursor.getLong(indexPersonId);
phoneCursor.close();
Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, personId);
bitmap = People.loadContactPhoto(activity, uri, R.drawable.icon,
null);
return bitmap;
}
return null;
}
with the help of method I getting contact photo that i use in my getView method. like
holder.imgViewLogo.setImageBitmap(getProfilepicture(activity,pos));
it's working proper but listview performance is low.
please help me out improve performance of listview.
I suggest moving the image loading to a background task.
From another thread, load all images to memory (or you can do some caching for only part of the list according to what is visible), once an image is ready, update the UI.
The functional behavior will be that at first, some images will not be shown immediately but performance will be much better.
Take a look here
Perhaps caching the data would improve performance? Try adding this to your code:
private static HashMap<String address, Bitmap image> cache = new HashMap<String address, Bitmap image>();
public Bitmap getProfilepicture(Activity activity, String address) {
if (cache.containskey(address) return cache.get(address);
Bitmap bitmap;
.
.
.
bitmap = People.loadContactPhoto(activity, uri, R.drawable.icon,
null);
cache.put(address,bitmap);
return bitmap;
}
This would at least prevent the bitmap from being created every time getView() is called.
Depending on the life of this particular class, you may or may not make the cache static and/or save its state to file for later loading.
you need to load the images asyncronesly.
here is a thread which discusses about this.
Lazy load of images in ListView
and this response has source code mentioned as source links
GitHub: https://github.com/thest1/LazyList
Source: http://open-pim.com/tmp/LazyList.zip
I am trying to display all the images stored in SD card in a Gallery View.
I have tried using the content provider (android.provider.MediaStore.images.Media class), but seem to be getting stuck at a point. Not sure if this is the way to go about it.
Here is my code so far:
String[] colsNeeded = new String[]{Media._ID, Media.TITLE};
Uri mMedia = Media.EXTERNAL_CONTENT_URI;
//Create a cursor using the URI & column names needd
Cursor c = managedQuery(mMedia, colsNeeded, null, null, Media.DATE_TAKEN + " ASC");
//What kind of adapter should I create here
//that contains images from the cursor??
SpinnerAdapter sa = null; //This is the point I get stuck
//Set the adapter of the gallery view
Gallery galleryPetPhotos = (Gallery)findViewById(R.id.GalleryPetPhotos);
galleryPetPhotos.setAdapter(sa);
Just a nudge in the right direction would be greatly appreciated.
This blog post has a good example on that. It shows you how to do the adapter: http://mihaifonoage.blogspot.com/2009/09/displaying-images-from-sd-card-in.html
However I would be extending a CursorAdapter instead of a BaseAdapter.