I have created a database of objects. Each of these objects contains two strings and two bitmaps. When I do a getAllContacts() call to my database, the loading takes quite a while. For me its not a problem, but for the end-users, this would be annoying. When I load an object I set this options to my bitmaps saying:
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
Which will shrink the Bitmaps to 1/8 of the original height and width. But still, it takes around 10-15 seconds to load 50 records and fill this into a ListView. Is there any way I can check if the object I try to load is in the memory, also called paging? Or maybe I can load the Bitmaps, when the users presses one of the items in the ListView?
Thanks in advance!
You set the sample size but it's still reading (I assume) large images. Why not set your bitmaps so that they are already small and then you can read small images without the scale factor applied?
I had a listView in my own code doing similar things and it was very slow until I wrote code to shrink each new image as it was created and then always just dealt with small images. And the code lived happily ever after.
The solution:
So, finally I found a solution to my problem. Instead of getting all of the Bitmaps when I do a getAllContact() call, the bitmap's is loaded when the user presses a row in the ListView This is whats done in the setOnClickListener method in CustomAdapter for my ListView
String PCN = cases.get(position).getCaseNumber();
int tempPCN = Integer.valueOf(PCN);
tempBitMapArray = dbHandler.getFinger(tempPCN);
Bitmap left = tempBitMapArray[0];
Bitmap right = tempBitMapArray[1];
ByteArrayOutputStream bs1 = new ByteArrayOutputStream();
left.compress(Bitmap.CompressFormat.PNG, 100, bs1);
ByteArrayOutputStream bs2 = new ByteArrayOutputStream();
right.compress(Bitmap.CompressFormat.PNG, 100, bs2);
And the getFinger(...) method:
public Bitmap[] getFinger(int pcn) {
Bitmap[] fingers = new Bitmap[2];
String SQLQUERY = "SELECT " + RIGHTFINGER + ", " + LEFTFINGER +" FROM " + TABLE_CASES + " WHERE " + KEY_ID + "=" + pcn + ";";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(SQLQUERY, null);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 2;
if (cursor.moveToFirst()) {
do {
byte[] blob1 = cursor.getBlob(cursor.getColumnIndexOrThrow("leftFinger"));
Bitmap bmp1 = BitmapFactory.decodeByteArray(blob1, 0, blob1.length, options);
byte[] blob2 = cursor.getBlob(cursor.getColumnIndexOrThrow("rightFinger"));
Bitmap bmp2 = BitmapFactory.decodeByteArray(blob2, 0, blob2.length, options);
fingers[0] = bmp1;
fingers[1] = bmp2;
return fingers;
} while(cursor.moveToNext());
}
return null;
}
I hope this example will give other people a pinpoint in the right direction.
Related
I want to take image in integer Array just like when you take R.drawable.img in integer array.
e.g : int[] images = {R.drawable.a , R.drawable.b , R.drawable.c , R.drawable.d};
I want same like this when you take image from server.
Declaration of Array:
String images[] = new String[1000];
Get Image from server and sets it to int array which is not happening.
public void onResponse(String response) {
Log.d("TAG", "Message Response: " + response.toString());
hideDialog();
try {
JSONObject jsonObj = new JSONObject(response);
boolean error = false;
p = jsonObj.getJSONArray("response");
for (int i = 0 ; i < p.length(); i++) {
JSONObject c = p.getJSONObject(i);
String profilepicture = c.getString("profile_picture");
byte[] decodedString = Base64.decode(profilepicture, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
Drawable drawable = new BitmapDrawable(getResources(),decodedByte);
images[i] = drawable;
}
The drawable values is android.graphics.drawable.BitmapDrawable#269a3b6d. But it not sets on the integer array.
You're getting a Drawable. You can't save a Drawable to an integer array or a String array- you have to make it a Drawable array. This approach is really NOT recommended btw- you're storing all of your images in memory, which will take a lot of memory. If its a non-trivial number you'll likely hit an OOM exception. For that matter, sending down all these images as Base64 encoded strings is not recommended either. The better way to do things is to have the server send down a URL of the image to download, and to download it only when you're sure you need it.
create an array for drawables then it would work, and you can get the image from that array via its integer index. but the case you are trying is that, you would have got that number BitmapDrawable#269a3b6d from debugging the code? right? this is a reference to the object in the memorey and not an integer! so don't try to put it in the integer array.
now either make your array type to drawable or change your scenario!
Hello kindhearted experts.
I am a newbie to Android Programming world.
I've asked a question about how to list images of phone on a GridView with three columns.
I finally got to the part where I am able to show my images on a GridView, but I am thinking my decoding method isn't efficient enough that it says "The application may be doing too much work on its main thread." and it is obvious the application is really slow.
here is my part of my code:
BitmapFactory.Options bo = new BitmapFactory.Options();
bo.inSampleSize = 8;
Bitmap bmp = BitmapFactory.decodeFile(thumbsDataList.get(position), bo);
Bitmap resized = Bitmap.createScaledBitmap(bmp, 95, 95, true);
imageView.setImageBitmap(resized);
Also, if this part helps you to figure out the issue:
private void getThumbInfo(ArrayList<String> thumbsIDs, ArrayList<String> thumbsDatas){
String[] proj = {MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE};
Cursor imageCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
proj, null, null, null);
if (imageCursor != null && imageCursor.moveToFirst()){
String thumbsID;
String thumbsImageID;
String thumbsData;
int thumbsIDCol = imageCursor.getColumnIndex(MediaStore.Images.Media._ID);
int thumbsDataCol = imageCursor.getColumnIndex(MediaStore.Images.Media.DATA);
int thumbsImageIDCol = imageCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME);
int num = 0;
do {
thumbsID = imageCursor.getString(thumbsIDCol);
thumbsData = imageCursor.getString(thumbsDataCol);
thumbsImageID = imageCursor.getString(thumbsImageIDCol);
num++;
if (thumbsImageID != null){
thumbsIDs.add(thumbsID);
thumbsDatas.add(thumbsData);
}
}while (imageCursor.moveToNext());
}
imageCursor.close();
return;
}
If anyone could give me an idea to fix this lag, I will appreciate it very much.
Thank you very much in advance.
I am developing an app that once a button is clicked I go to a new activity where 3 random images are set to an imageView. If the user does not like the 3 images selected, they can then click a button to generate 3 new random images. I am having two problems...
I have the imageView set to a specific size but when the images show up they are sometimes smaller and are showing horizontally. I would like them to show up vertically and all as the size of the imageView. How do I change the code to do this?
I am getting the new images to show up when I click the button but I am getting an error saying that I am running out of memory after I do it a few times. How do I change the code so I stop using all my memory?
Here is my code
public ImageView shirts;
public ImageView pants;
public ImageView shoes;
Button reload;
private Bitmap currentBitmap = null;
String[] projection = new String[]{
MediaStore.Images.Media.DATA,
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.suggested_outfit);
shirts = (ImageView)findViewById(R.id.shirtImage);
pants = (ImageView)findViewById(R.id.pantsImage);
shoes = (ImageView)findViewById(R.id.shoesImage);
shirts.setImageBitmap(getImage());
pants.setImageBitmap(getImage());
shoes.setImageBitmap(getImage());
reload = (Button)findViewById(R.id.getNewOutfit);
reload.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
startActivity(getIntent());
}
});
}
public Bitmap getImage () {
Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Cursor cur = getContentResolver().query(images,
projection,"", null, ""
);
final ArrayList<String> imagesPath = new ArrayList<String>();
if (cur.moveToFirst()) {
int dataColumn = cur.getColumnIndex(
MediaStore.Images.Media.DATA);
do {
imagesPath.add(cur.getString(dataColumn));
} while (cur.moveToNext());
}
cur.close();
final Random random = new Random();
final int count = imagesPath.size();
int number = random.nextInt(count);
String path = imagesPath.get(number);
currentBitmap = BitmapFactory.decodeFile(path);
return currentBitmap;
}
}
I read that it isn't good practice to reload the activity but I can't figure out a way to generate 3 new random images. I am open for any suggestions if there is a better way to do this.
Obtaining images through an onClick method would have to have
do {
String path = cur.getString(dataColumn);
if(path.contains("yourDirectory")){ //in your case, your image directory
imagesPath.add(path);
}
} while (cur.moveToNext());
within the requested intent/activity
Decoding Bitmap consumes much memory, on your code you decode Bitmap many times when click button maybe causes OutOfMemory. Try to decode all your Bitmap once times and save them to a list. When a click to generate random Bitmap, you create random index of the list Bitmap and retrieve object by that index. Hope this help!
you can use this utils: image loader
it has memory manager.and you can set imageview when image load completed.
My app takes a photo using the camera. It then gets the URI, then the path of that photo and stores the path in a SQLite database. All of that seems to work fine, however when I go to view the images as bitmaps, this code only shows the same image (the last one) for every record (all others informations on each record displays correctly).
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
mPhoto = BitmapFactory.decodeFile(timage.toString(), options);
viewHolder.img_image = (ImageView) convertView.findViewById(R.id.image);
viewHolder.img_image.setImageBitmap(mPhoto);
This is where I get the information for timage, and the rest of the record details :
public ArrayList<ProductModel> _productlist = new ArrayList<ProductModel>();
ArrayList<ProductModel> product_list = db.getProducts();
for (int i = 0; i < product_list.size(); i++) {
String tidno = product_list.get(i).getIdno();
System.out.println("tidno>>>>>" + tidno);
timage = product_list.get(i).getImage();
System.out.println(timage);
String tname = product_list.get(i).getProductname();
String tdesc = product_list.get(i).getproductdesc();
ProductModel _ProductModel = new ProductModel();
_ProductModel.setIdno(tidno);
_ProductModel.setImage(timage);
_ProductModel.setProductname(tname);
_ProductModel.setproductdesc(tdesc);
_productlist.add(_ProductModel);
}
totalrecords.setText("Total Records :-" + _productlist.size());
listview.setAdapter(new ListAdapter(this));
db.close();
The first 3 lines are based on Vino's answer from Suggestions to avoid bitmap Out of Memory error - scaling the images to get rid of the out of memory errors I was receiving otherwise.
mPhoto is simply a Bitmap variable, timage is the images paths (which prints onto the console correctly).
Anyone able to give me a hint in the right direction to get the app showing every image, rather than just the last one taken ?
That variable timage is conserving the value of the last item in the for iteration.
for (int i = 0; i < product_list.size(); i++)
{
//...
timage = product_list.get(i).getImage();
//...
}
So after exiting the loop timage will have the value of the last item. And of course here
mPhoto = BitmapFactory.decodeFile(timage.toString(), options);
it's being used for all your images. Shouldn't you be using _ProductModel.getImage() instead?
Hi all,
I have searched a lot for my problem, I found a lot of posts with similar problems, but no one gave me a correct solution.
What I want is a gridview displaying a sdcard folder's images. I also have to offer the possibility to take a picture, and when going back to the gridview, update it with the new picture.
To take the picture, I use this code :
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getImageFileUri());
startActivityForResult(intent, TAKE_PICTURE_ACTIVITY);
Where getImageFileUri() is a function giving me a picture name with a timestamp, using Environment.getExternalStorageDirectory() to get the sdcard path, and checking if the folder exists (and create it if it doesn't).
For the moment, I use a cursor to get my images :
private void displayGallery() {
// Query params :
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.Media._ID};
String selection = MediaStore.Images.Media.DATA + " like ? ";
String[] selectionArgs = {"%Otiama%"};
// Submit the query :
mCursor = managedQuery(uri, projection, selection, selectionArgs, null);
if (mCursor != null) {
mGridView.setOnItemClickListener(this);
mGridView.setAdapter(new ImageAdapter(this, mCursor));
}
else showToast("Gallery is empty : " + uri.toString());
}
And here is my adapter's getView :
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
// Move cursor to current position
mCursor.moveToPosition(position);
// Get the current value for the requested column
int columnIndex = mCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int imageID = mCursor.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(mContext.getContentResolver(), originalImageId, MediaStore.Images.Thumbnails.MINI_KIND, null);
imageView.setImageBitmap(b);
return imageView;
}
This code works, but is very slow and doesn't update (new pictures's thumbnails aren't created), even if I call again my displayGallery() function in the onActivityResult(). Well, it doesn't update even if I reload the app >< . I have to run it again from eclipse.
In fact, what I would like is the same behavior than ES File Explorer (when you open a folder, the pictures have all a preview image, and they are loaded asynchronously), which, I think, doesn't use those ** thumbnails.
So I tried to load the pictures as bitmaps using the bitmap factory, but even with a few pictures (1-2), I instantly get a "java.lang.OutOfMemoryError: bitmap size exceeds VM budget"... I guess I have to resize them, but if I do so, won't I have the same error when I will load 20-30 pictures ?? Or the problem is that each picture exceeds the budget, and so if I resize them I will avoid this error for all of them ?
Sorry for the big post, if someone can help...
Well, I answer myself :
I created my own thumbnails this way :
public static Bitmap resizedBitmap(Bitmap bitmap, int newWidth) {
// Get the bitmap size :
int width = bitmap.getWidth();
int height = bitmap.getHeight();
double ratio = (double)width / (double)height;
// Compute the thumbnail size :
int thumbnailHeight = newWidth;
int thumbnailWidth = (int) ( (double)thumbnailHeight * ratio);
// Create a scaled bitmap :
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, thumbnailWidth, thumbnailHeight, false);
return scaledBitmap;
}
I don't use cursors anymore, to load the thumbnails I proceed like this (in the ImageAdapter) :
public void loadThumbnails() {
// Init the ArrayList :
_thumbnails = new ArrayList<ImageView>();
_imagesNames = new ArrayList<String>();
// Run through the thumbnails dir :
File imagesThumbnailsDir = new File(_imagesThumbnailsDirUri.getPath());
File[] imagesThumbnails = imagesThumbnailsDir.listFiles();
Arrays.sort(imagesThumbnails);
// For each thumbnail :
for(File imageThumbnail : imagesThumbnails)
{
// Check if the image exists :
File image = new File(_imagesDirUri.getPath() + File.separator + imageThumbnail.getName());
if(image.exists()) {
ImageView imageView = new ImageView(_context);
imageView.setImageDrawable(Drawable.createFromPath(imageThumbnail.getAbsolutePath()));
_thumbnails.add(imageView);
_imagesNames.add(imageThumbnail.getName());
}
// If not, delete the thumbnail :
else {
ImageUtils.deleteFile(Uri.fromFile(imageThumbnail));
}
}
}
And so my ImageAdapter's getView function sounds like this :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(_context);
imageView.setLayoutParams(new GridView.LayoutParams(80, 80));
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageDrawable(_thumbnails.get(position).getDrawable());
return imageView;
}
Hope it helps..