How to display properly sized image from user gallery? - android

I am struggling with showing the proper sized images in two of my activities.
The image in the CatalogActivity looks like it is properly scaled down and then cropped while the image in the EditorActivity is whole and is much smaller.
The only difference is that the image in the catalog comes from image resource while the editor image comes from the gallery so from Uri.
Could you tell me why is this difference and how to make the image from EditorActivity the same as the other one?
From CatalogActivity:
// Make the sample image smaller so it doesn't take too much space in the memory
Bitmap sourdoughBitmap = ProductsEntry.decodeSampledBitmapFromResource(getResources(), R.drawable.sourdough_picture, 72, 72);
From EditorActivity:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == RESULT_LOAD_PICTURE && resultCode == RESULT_OK && null != intent) {
Uri selectedPictureUri = intent.getData();
// Show the selected picture in an ImageView in the editor
try {
// Scale the bitmap received from the uri so it fits in the small ImageView
scaledPictureBitmap = ProductsEntry.decodeSampledBitmapFromUri(this, selectedPictureUri, 72, 72);
// Hide the gray picture placeholder
mImageView.setBackgroundResource(0);
// Show the scaled bitmap in the ImageView
mImageView.setImageBitmap(scaledPictureBitmap);
// The user has chosen a picture and we can change
// the text of the button to say "Change picture"
mAddPictureButton.setText(R.string.edit_product_change_photo);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
From ProductsEntry:
/**
* Helps create a smaller bitmap image from resource
* #param resources - a resources object
* #param resourceId - the id of the image in the drawable folder
* #param requiredWidth - the width that we want for the final image
* #param requiredHeight - the height that we want for the final image
* #return the decoded Bitmap
*/
public static Bitmap decodeSampledBitmapFromResource(Resources resources, int resourceId,
int requiredWidth, int requiredHeight){
// First decode with inJustDecodeBounds = true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources, resourceId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
// Decode bitmap with inJustDecodeBounds = false
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(resources, resourceId, options);
}
/**
* Helps create a smaller bitmap image from a uri
* #param context - a context object
* #param uri - the uri of the image
* #param requiredWidth - the width that we want for the final image
* #param requiredHeight - the height that we want for the final image
* #return the decoded Bitmap
*/
public static Bitmap decodeSampledBitmapFromUri(Context context, Uri uri, int requiredWidth, int requiredHeight)
throws FileNotFoundException {
// First decode with inJustDecodeBounds = true, only to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri), null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
// Decode bitmap with inJustDecodeBounds = false
options.inJustDecodeBounds = false;
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri), null, options);
}
/**
* Calculate a sample size value that is a power of 2 based on a target width and height
* #param options is used to pass options to the BitmapFactory
* #param requiredWidth is the width that we want for the final image
* #param requiredHeight is the height that we want for the final image
* #return by how much to scale down the image
*/
private static int calculateInSampleSize(BitmapFactory.Options options, int requiredWidth, int requiredHeight) {
// Raw height and width of the image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > requiredHeight || width > requiredWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the required height and width
while ((halfHeight / inSampleSize) >= requiredHeight
&& (halfWidth / inSampleSize) >= requiredWidth){
inSampleSize *= 2;
}
}
return inSampleSize;
}
From activity_editor.xml:
<ImageButton
android:id="#+id/picture"
android:layout_height="72dp"
android:layout_width="72dp"
android:src="#drawable/ic_photo"
android:layout_gravity="center" />
Link to the repository here.

Related

Choose image size in camera intent

I'm new to this forum, so please bear with me and gently point out mistakes if any,
So I'm working on a project where I'm uploading images to server, now I want to limit the size of images, I'm giving an option to "Click image" where my code will open default camera intent and clicks the pic, or "Choose from gallery".
My question is regarding "Click image", Now when user clicks an image, can I preset the image max size which can be clicked?
You can resize your image in onActivityResult method,try following code snippet
public static Bitmap handleSamplingAndRotationBitmap(Context context, Uri selectedImage)
throws IOException {
int MAX_HEIGHT = 1024;
int MAX_WIDTH = 1024;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream imageStream = context.getContentResolver().openInputStream(selectedImage);
BitmapFactory.decodeStream(imageStream, null, options);
imageStream.close();
options.inSampleSize = calculateInSampleSizes(options, MAX_WIDTH, MAX_HEIGHT);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
imageStream = context.getContentResolver().openInputStream(selectedImage);
Bitmap img = BitmapFactory.decodeStream(imageStream, null, options);
img = rotateImageIfRequired(context, img, selectedImage);
return img;
}
private static int calculateInSampleSizes(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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee a final image
// with both dimensions larger than or equal to the requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down further
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
private static Bitmap rotateImageIfRequired(Context context, Bitmap img, Uri selectedImage) throws IOException {
InputStream input = context.getContentResolver().openInputStream(selectedImage);
ExifInterface ei;
if (Build.VERSION.SDK_INT > 23)
ei = new ExifInterface(input);
else
ei = new ExifInterface(selectedImage.getPath());
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
you just need invoke handleSamplingAndRotationBitmap method,you'll get a Bitmap which size can be set by yourself.
PS: Case some pictures captured by sumsung's phone 's rotation is incorrect,so we need handle picture's orientation too,hope that can help you.
You can simply get the size of the file. You need to store the image when u take for it. after that u can get size using below code segment
String imagePath = Environment.getExternalStorageDirectory() + "/yourImagefile.png";
File imageFile = new File(imagePath );
long filelength = imageFile .length();
length = filelength/1024;
this length give you size in KB. then you can add if condition like below
if(length>sizeyouwant){
//delete image and toast message with info
if(imageFile.exists()) {
imageFile.delete();
}
Toast.makeText(getApplicationContext(),
"Image is not saved due to image size exceeds limit....",
Toast.LENGTH_SHORT).show();
}

Android get image from path (gallery, pictures ect)

Need to get image from path. I have tried everything but don't seem to get the image.
My two image paths:
/storage/emulated/0/DCIM/Camera/20161025_081413.jpg
content://media/external/images/media/4828
How do i set my image from these paths?
I am using ImageView to display my image.
My code:
File imgFile = new File("/storage/emulated/0/DCIM/Camera/20161025_081413.jpg");
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
holder.myimage.setImageBitmap(myBitmap);
Thanks in advance
Regularly, you can just write BitmapFactory.decodeBitmap(....) etc, but the file can be huge and you can get the OutOfMemoryError in no time, especially, if you do decoding a few times in the row. So you need to compress the image before setting it to view, so you won't run out of memory. Here is the proper way to do it.
File f = new File(path);
if(file.exists()){
Bitmap myBitmap = ImageHelper.getCompressedBitmap(photoView.getMaxWidth(), photoView.getMaxHeight(), f);
photoView.setImageBitmap(myBitmap);
}
//////////////
/**
* Compresses the file to make a bitmap of size, passed in arguments
* #param width width you want your bitmap to have
* #param height hight you want your bitmap to have.
* #param f file with image
* #return bitmap object of sizes, passed in arguments
*/
public static Bitmap getCompressedBitmap(int width, int height, File f) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(f.getAbsolutePath(), options);
options.inSampleSize = calculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(f.getAbsolutePath(), options);
}
/////////////////
public static 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) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
I found my problem. I am using Android SDK 23.
From the Android documentation.
If the device is running Android 6.0 or higher, and your app's target SDK is 23 or higher: The app has to list the permissions in the manifest, and it must request each dangerous permission it needs while the app is running. The user can grant or deny each permission, and the app can continue to run with limited capabilities even if the user denies a permission request.
https://developer.android.com/training/permissions/requesting.html
Hopes this helps someone else

ImageView dimensions

I have following situation:
I have ImageView and Bitmap inside it.
The problem is that
I don't know the dimensions of bitmap (not physical size, size that it takes on screen)
or
I need ImageView not to be bigger that this Bitmap on the screen.
ImageView's background has black color on below snapshot.
You can make sure the image view isn't bigger than the bitmap by setting its layout_width and layout_height to wrap_content on your imageView tag in its xml file.
You can also use its scaleType to affect how the image should be manipulated to fit the imageView.
You can also just access the bitmap's width/height properties to get its dimensions.
EDIT::
You can convert your bitmap into a byte[] and resize it using the following helpers:
/**
* Resize an image to a specified width and height.
* #param targetWidth The width to resize to.
* #param targetHeight The height to resize to.
* #return The resized image as a Bitmap.
* */
public static Bitmap resizeImage(byte[] imageData, int targetWidth, int targetHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight);
options.inJustDecodeBounds = false;
Bitmap reducedBitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
return Bitmap.createScaledBitmap(reducedBitmap, targetWidth, targetHeight, false);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int requestedWidth, int requestedHeight) {
// Get the image's raw dimensions
final int rawHeight = options.outHeight;
final int rawWidth = options.outWidth;
int inSampleSize = 1;
if (rawHeight > requestedHeight || rawWidth > requestedWidth) {
final int halfHeight = rawHeight / 2;
final int halfWidth = rawWidth / 2;
/*
* Calculate the largest inSampleSize value that is a power of 2 and keeps both
* height and width larger than their requested counterparts respectively.
* */
while ((halfHeight/inSampleSize) > requestedHeight && (halfWidth/inSampleSize) > requestedWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}

Android Out of Memory error on drawable folder [duplicate]

This question already has an answer here:
Bitmap too large to be uploaded into a texture
(1 answer)
Closed 7 years ago.
In my android app, have all the images in the drawable folder. On most phones, we had no issue. But some phones have out of memory error. When the images copying for example to the drawable-xhdpi folder the issue is gone. What is the reason this problem, how can i fix it?
drawable is equivalent of drawable-mdpi
If you put your images in that folder they will get up-sampled for higher resolutions devices and that up-sampling can trigger OOM if images are large.
If you put same sized images in drawable-xhdpi you will have upsampled images only on larger xxhdpi devices, and downsampled on others.
If you want to avoid automatic up/down sampling of images put them in drawable-nodpi folder.
Different devices might have different size limits. Try using: drawable-xxhdpi
Helpful cheat sheet: http://i.stack.imgur.com/kV4Oh.png
For managing Out Of Memory Error one thing you may need to do is reduce the image size by compressing it and keep it in drawable folders.which is useful to reduce the app size and also memory consumption at runtime.
Or you may need to use the following class for reducing the image size aspect ratio.
ImageResizer
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight, boolean isLow) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
if (isLow) {
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
/**
* Calculate an inSampleSize for use in a {#link BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from {#link BitmapFactory}. This implementation calculates
* the closest inSampleSize that is a power of 2 and will result in the final decoded bitmap
* having a width and height equal to or larger than the requested width and height.
*
* #param options An options object with out* params already populated (run through a decode*
* method with inJustDecodeBounds==true
* #param reqWidth The requested width of the resulting bitmap
* #param reqHeight The requested height of the resulting bitmap
* #return The value to be used for inSampleSize
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// BEGIN_INCLUDE (calculate_sample_size)
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger inSampleSize).
long totalPixels = width * height / inSampleSize;
// Anything more than 2x the requested pixels we'll sample down further
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2;
totalPixels /= 2;
}
}
return inSampleSize;
// END_INCLUDE (calculate_sample_size)
}
Usage
private Bitmap mBackground;
private Drawable mBackgroundDrawable;
#Override
protected void onCreate(Bundle savedInstanceState) {
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.parent);
final Resources res = getResources();
int[] dimensions = Util.getDisplayDimensions(this);
mBackground = ImageResizer.decodeSampledBitmapFromResource(res, R.drawable.bg, 100, 100, false);
mBackgroundDrawable = new BitmapDrawable(res, mBackground);
linearLayout.setBackground(mBackgroundDrawable);
}
#Override
protected void onDestroy() {
recycle();
super.onDestroy();
}
private void recycle() {
if (mBackground != null) {
mBackground.recycle();
mBackground = null;
if (mBackgroundDrawable != null)
mBackgroundDrawable = null;
}
}
Note : If your applying true as third argument which help you to reduce the image size effectively using Bitmap.Config.RGB_565.
if (isLow) {
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
Finally, research about OOM.

Android Compress a Bitmap

I have wrote this method before I noticed there is a compress method in Bitmap class.
/**
* Calcuate how much to compress the image
* #param options
* #param reqWidth
* #param reqHeight
* #return
*/
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1; // default to not zoom image
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height/ (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
/**
* resize image to 480x800
* #param filePath
* #return
*/
public static Bitmap getSmallBitmap(String filePath) {
File file = new File(filePath);
long originalSize = file.length();
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize based on a preset ratio
options.inSampleSize = calculateInSampleSize(options, 480, 800);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap compressedImage = BitmapFactory.decodeFile(filePath, options);
return compressedImage;
}
I was wondering, compare to the built in Compress method, should I keep using this one, or switch to use the built in one? what is the difference?
Your method is in line with Loading Large Bitmap guidelines
Large file on disk
Small bitmap in memory
compress() methods converts a large bitmap to a small one:
Large bitmap in memory
Small bitmap on disk (IOStream) (and possibly in different format)
I would use your method if I needed to load bitmap from a file to ImageViews of different sizes.
Basically
What you are doing in the above code is just resizing the image ,which will not loose much of the quality of the image since you use the SampleSize .
compress(Bitmap.CompressFormat format, int quality, OutputStream stream)
It is used when you want to change the imageFormat you have Bitmap.CompressFormat JPEG
Bitmap.CompressFormat PNG Bitmap.CompressFormat WEBP or to reduce the quality of the image using the quality parameter 0 - 100 .

Categories

Resources