I have a square ImageView which displays pictures of varying dimensions. I want to always maintain the original aspect ratio of the pictures and have no margin around the image (so that the image takes up the whole ImageView). For this, I am using the centerCrop scaleType on the ImageView. However, I want to make it so that if the top and bottom of the image are cut off (i.e.: the image is taller than it is wide), the image gets pulled towards the bottom of the container. So instead of having equal amounts of pixels cropped at the top and bottom, the image is flush with the top and sides of the ImageView and the bottom of the image has twice as much cropped off. Is this possible in xml, if not, is there a java solution?
You won't be able to do that with a regular ImageView and it's properties in xml. You can accomplish that with a proper scaleType Matrix, but tbh writing it is a pain in the ass. I'd suggest you use a respected library that can handle this easily. For example CropImageView.
You probably can't do this in layout. But it's possible with a piece of code like this:
final ImageView image = (ImageView) findViewById(R.id.image);
// Proposing that the ImageView's drawable was set
final int width = image.getDrawable().getIntrinsicWidth();
final int height = image.getDrawable().getIntrinsicHeight();
if (width < height) {
// This is just one of possible ways to get a measured View size
image.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int measuredSize = image.getMeasuredWidth();
int offset = (int) ((float) measuredSize * (height - width) / width / 2);
image.setPadding(0, offset, 0, -offset);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
image.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
image.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
}
Note that if your ImageView has predefined size (likely it has) then you need to put this size to dimen resources and the code will be even simpler:
ImageView image = (ImageView) findViewById(R.id.image2);
// For sure also proposing that the ImageView's drawable was set
int width = image.getDrawable().getIntrinsicWidth();
int height = image.getDrawable().getIntrinsicHeight();
if (width < height) {
int imageSize = getResources().getDimensionPixelSize(R.dimen.image_size);
int offset = (int) ((float) imageSize * (height - width) / width / 2);
image.setPadding(0, offset, 0, -offset);
}
See also:
findViewById()
getResources()
I am taking a picture using camera in my app and will email the picture to a certain email address. now i want the emailed picture's physical dimensions to be 10cm's in width and 8cm's in height. and the code i have tried is as follows:
1) declare a imageview in xml like below :
<ImageView
android:id="#+id/imageView3"
android:layout_width="100mm"
android:layout_height="80mm"
android:src="#drawable/ic_launcher" />
2) and later in java file :
final BitmapFactory.Options opts = new BitmapFactory.Options ();
opts.inSampleSize = 2;
final ImageView thumbNail = (ImageView)findViewById(R.id.imageView3);
thumbNail.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
thumbNail.setImageBitmap(Bitmap.createScaledBitmap (BitmapFactory.decodeFile(myPhoto.getPath(), opts), thumbNail.getWidth(), thumbNail.getHeight(), false));
}
});
As per the documentation the resulting bitmap should have width of 10cm's and height of 8cm's. but in xml file there is warning at these lines
android:layout_width="100mm"
android:layout_height="80mm"
Avoid using "mm" as units (it does not work accurately on all devices); use "dp" instead.
so i don't have a clue as to what i should do. so please give me a solution that will work on all devices.
This?
public static int mm2pixels(int val, Context current) {
DisplayMetrics metrics = current.getResources().getDisplayMetrics();
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, val, metrics);
}
i have used WarrenFaiths directions and came up with the below solution.
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
totalDIP_X = metrics.xdpi;
totalDIP_Y = metrics.ydpi;
totalDIP_X and totalDIP_Y represent the total number of DIPs per inch on the phone.
so if you want the bitmap to be 5cms*5cms on phone, which is equivalent to 1.968inch*1.968inch, we should use the below code.
imageView1.setImageBitmap(Bitmap.createScaledBitmap (aBitmap, (int)(1.968*totalDIP_X), (int)(1.968*totalDIP_Y), false));
I cant figure out how to use a bitmap as background image in a way that it is scaled to fill the screen but keeps the aspect ratio. So far I have only found the solution that I use an additional ImageView instead of a background image and use android:scaleType="centerCrop"
...
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/desc_background"
android:scaleType="centerCrop"
android:src="#drawable/background"
/>
...
Nevertheless I would prefer to solve this by setting the activity background on the window
<style ...>
<item name="android:windowBackground">#drawable/background</item>
as I was told (and see) that this is much faster. (#drawable/background being a XML drawable)
Seems like such a common task yet I cant find a solution so far. Any advice would be highly appreciated.
Thanks
martin
Did you try to set adjustViewBounds property to true ?
http://developer.android.com/reference/android/widget/ImageView.html#attr_android:adjustViewBounds
I have stumbled upon the same problem a number of times in my projects and each time due to lack of time (and laziness) I would be satisfied with a less than optimum solution. But recently I found some time to crack down this particular issue. Here is my solution and I hope it helps you as well.
Bitmap scaleDownLargeImageWithAspectRatio(Bitmap image)
{
int imaheVerticalAspectRatio,imageHorizontalAspectRatio;
float bestFitScalingFactor=0;
float percesionValue=(float) 0.2;
//getAspect Ratio of Image
int imageHeight=(int) (Math.ceil((double) image.getHeight()/100)*100);
int imageWidth=(int) (Math.ceil((double) image.getWidth()/100)*100);
int GCD=BigInteger.valueOf(imageHeight).gcd(BigInteger.valueOf(imageWidth)).intValue();
imaheVerticalAspectRatio=imageHeight/GCD;
imageHorizontalAspectRatio=imageWidth/GCD;
Log.i("scaleDownLargeImageWIthAspectRatio","Image Dimensions(W:H): "+imageWidth+":"+imageHeight);
Log.i("scaleDownLargeImageWIthAspectRatio","Image AspectRatio(W:H): "+imageHorizontalAspectRatio+":"+imaheVerticalAspectRatio);
//getContainer Dimensions
int displayWidth = getWindowManager().getDefaultDisplay().getWidth();
int displayHeight = getWindowManager().getDefaultDisplay().getHeight();
//I wanted to show the image to fit the entire device, as a best case. So my ccontainer dimensions were displayWidth & displayHeight. For your case, you will need to fetch container dimensions at run time or you can pass static values to these two parameters
int leftMargin = 0;
int rightMargin = 0;
int topMargin = 0;
int bottomMargin = 0;
int containerWidth = displayWidth - (leftMargin + rightMargin);
int containerHeight = displayHeight - (topMargin + bottomMargin);
Log.i("scaleDownLargeImageWIthAspectRatio","Container dimensions(W:H): "+containerWidth+":"+containerHeight);
//iterate to get bestFitScaleFactor per constraints
while((imageHorizontalAspectRatio*bestFitScalingFactor <= containerWidth) &&
(imaheVerticalAspectRatio*bestFitScalingFactor<= containerHeight))
{
bestFitScalingFactor+=percesionValue;
}
//return bestFit bitmap
int bestFitHeight=(int) (imaheVerticalAspectRatio*bestFitScalingFactor);
int bestFitWidth=(int) (imageHorizontalAspectRatio*bestFitScalingFactor);
Log.i("scaleDownLargeImageWIthAspectRatio","bestFitScalingFactor: "+bestFitScalingFactor);
Log.i("scaleDownLargeImageWIthAspectRatio","bestFitOutPutDimesions(W:H): "+bestFitWidth+":"+bestFitHeight);
image=Bitmap.createScaledBitmap(image, bestFitWidth,bestFitHeight, true);
//Position the bitmap centre of the container
int leftPadding=(containerWidth-image.getWidth())/2;
int topPadding=(containerHeight-image.getHeight())/2;
Bitmap backDrop=Bitmap.createBitmap(containerWidth, containerHeight, Bitmap.Config.RGB_565);
Canvas can = new Canvas(backDrop);
can.drawBitmap(image, leftPadding, topPadding, null);
return backDrop;
}
I'm loading a bitmap into an ImageView, and seeing this error. I gather this limit relates to a size limit for OpenGL hardware textures (2048x2048). The image I need to load is a pinch-zoom image of about 4,000 pixels high.
I've tried turning off hardware acceleration in the manifest, but no joy.
<application
android:hardwareAccelerated="false"
....
>
Is it possible to load an image larger than 2048 pixels into an ImageView?
This isn't a direct answer to the question (loading images >2048), but a possible solution for anyone experiencing the error.
In my case, the image was smaller than 2048 in both dimensions (1280x727 to be exact) and the issue was specifically experienced on a Galaxy Nexus. The image was in the drawable folder and none of the qualified folders. Android assumes drawables without a density qualifier are mdpi and scales them up or down for other densities, in this case scaled up 2x for xhdpi. Moving the culprit image to drawable-nodpi to prevent scaling solved the problem.
I have scaled down the image in this way:
ImageView iv = (ImageView)waypointListView.findViewById(R.id.waypoint_picker_photo);
Bitmap d = new BitmapDrawable(ctx.getResources() , w.photo.getAbsolutePath()).getBitmap();
int nh = (int) ( d.getHeight() * (512.0 / d.getWidth()) );
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
iv.setImageBitmap(scaled);
All rendering is based on OpenGL, so no you can't go over this limit (GL_MAX_TEXTURE_SIZE depends on the device, but the minimum is 2048x2048, so any image lower than 2048x2048 will fit).
With such big images, if you want to zoom in out, and in a mobile, you should setup a system similar to what you see in google maps for example. With the image split in several pieces, and several definitions.
Or you could scale down the image before displaying it (see user1352407's answer on this question).
And also, be careful to which folder you put the image into, Android can automatically scale up images. Have a look at Pilot_51's answer below on this question.
Instead of spending hours upon hours trying to write/debug all this downsampling code manually, why not use Picasso? It was made for dealing with bitmaps of all types and/or sizes.
I have used this single line of code to remove my "bitmap too large...." problem:
Picasso.load(resourceId).fit().centerCrop().into(imageView);
Addition of the following 2 attributes in (AndroidManifest.xml) worked for me:
android:largeHeap="true"
android:hardwareAccelerated="false"
Changing the image file to drawable-nodpi folder from drawable folder worked for me.
I used Picasso and had the same problem. image was too large at least in on size, width or height. finally I found the solution here. you can scale the large image down according to display size and also keep the aspect ratio:
public Point getDisplaySize(Display display) {
Point size = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(size);
} else {
int width = display.getWidth();
int height = display.getHeight();
size = new Point(width, height);
}
return size;
}
and use this method for loading image by Picasso:
final Point displySize = getDisplaySize(getWindowManager().getDefaultDisplay());
final int size = (int) Math.ceil(Math.sqrt(displySize.x * displySize.y));
Picasso.with(this)
.load(urlSource)
.resize(size, size)
.centerInside()
.into(imageViewd);
also for better performance you can download the image according to width and height of the display screen, not whole the image:
public String reviseImageUrl(final Integer displayWidth, final Integer displayHeight,
final String originalImageUrl) {
final String revisedImageUrl;
if (displayWidth == null && displayHeight == null) {
revisedImageUrl = originalImageUrl;
} else {
final Uri.Builder uriBuilder = Uri.parse(originalImageUrl).buildUpon();
if (displayWidth != null && displayWidth > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_WIDTH, String.valueOf(displayWidth));
}
if (displayHeight != null && displayHeight > 0) {
uriBuilder.appendQueryParameter(QUERY_KEY_DISPLAY_HEIGHT, String.valueOf(displayHeight));
}
revisedImageUrl = uriBuilder.toString();
}
return revisedImageUrl;
}
final String newImageUlr = reviseImageUrl(displySize.x, displySize.y, urlSource);
and then:
Picasso.with(this)
.load(newImageUlr)
.resize(size, size)
.centerInside()
.into(imageViewd);
EDIT: getDisplaySize()
display.getWidth()/getHeight() is deprecated. Instead of Display use DisplayMetrics.
public Point getDisplaySize(DisplayMetrics displayMetrics) {
int width = displayMetrics.widthPixels;
int height = displayMetrics.heightPixels;
return new Point(width, height);
}
BitmapRegionDecoder does the trick.
You can override onDraw(Canvas canvas), start a new Thread and decode the area visible to the user.
As pointed by Larcho, starting from API level 10, you can use BitmapRegionDecoder to load specific regions from an image and with that, you can accomplish to show a large image in high resolution by allocating in memory just the needed regions. I've recently developed a lib that provides the visualisation of large images with touch gesture handling. The source code and samples are available here.
View level
You can disable hardware acceleration for an individual view at runtime with the following code:
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I ran through same problem, here is my solution. set the width of image same as android screen width and then scales the height
Bitmap myBitmap = BitmapFactory.decodeFile(image.getAbsolutePath());
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Log.e("Screen width ", " "+width);
Log.e("Screen height ", " "+height);
Log.e("img width ", " "+myBitmap.getWidth());
Log.e("img height ", " "+myBitmap.getHeight());
float scaleHt =(float) width/myBitmap.getWidth();
Log.e("Scaled percent ", " "+scaleHt);
Bitmap scaled = Bitmap.createScaledBitmap(myBitmap, width, (int)(myBitmap.getWidth()*scaleHt), true);
myImage.setImageBitmap(scaled);
This is better for any size android screen. let me know if it works for you.
Scale down image:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);
int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
pow += 1;
options.inSampleSize = 1 << pow;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);
The image will be scaled down at the size of reqHeight and reqWidth. As I understand inSampleSize only take in a power of 2 values.
Use Glide library instead of directly loading into imageview
Glide : https://github.com/bumptech/glide
Glide.with(this).load(Uri.parse(filelocation))).into(img_selectPassportPic);
I tried all the solutions above, one-after-the-other, for quite many hours, and none seemed to work! Finally, I decided to look around for an official example concerning capturing images with Android's camera, and displaying them. The official example (here), finally gave me the only method that worked. Below I present the solution I found in that example app:
public void setThumbnailImageAndSave(final ImageView imgView, File imgFile) {
/* There isn't enough memory to open up more than a couple camera photos */
/* So pre-scale the target bitmap into which the file is decoded */
/* Get the size of the ImageView */
int targetW = imgView.getWidth();
int targetH = imgView.getHeight();
/* Get the size of the image */
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
/* Figure out which way needs to be reduced less */
int scaleFactor = 1;
if ((targetW > 0) || (targetH > 0)) {
scaleFactor = Math.min(photoW/targetW, photoH/targetH);
}
/* Set bitmap options to scale the image decode target */
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
/* Decode the JPEG file into a Bitmap */
Bitmap bitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bmOptions);
/* Associate the Bitmap to the ImageView */
imgView.setImageBitmap(bitmap);
imgView.setVisibility(View.VISIBLE);
}
NOTE FOR THOSE WHO WANT TO PUT IMAGES OF SMALL SIZE:
Pilot_51's solution (moving your images to drawable-nodpi folder) works, but has another problem:
It makes images TOO SMALL on screen unless the images are resized to a very large (like 2000 x 3800) resolution to fit screen -- then it makes your app heavier.
SOLUTION: put your image files in drawable-hdpi -- It worked like a charm for me.
Using the correct drawable subfolder solved it for me. My solution was to put my full resolution image (1920x1200) into the drawable-xhdpi folder, instead of the drawable folder.
I also put a scaled down image (1280x800) into the drawable-hdpi folder.
These two resolutions match the 2013 and 2012 Nexus 7 tablets I'm programming. I also tested the solution on some other tablets.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
///*
if (requestCode == PICK_FROM_FILE && resultCode == RESULT_OK && null != data){
uri = data.getData();
String[] prjection ={MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri,prjection,null,null,null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(prjection[0]);
ImagePath = cursor.getString(columnIndex);
cursor.close();
FixBitmap = BitmapFactory.decodeFile(ImagePath);
ShowSelectedImage = (ImageView)findViewById(R.id.imageView);
// FixBitmap = new BitmapDrawable(ImagePath);
int nh = (int) ( FixBitmap.getHeight() * (512.0 / FixBitmap.getWidth()) );
FixBitmap = Bitmap.createScaledBitmap(FixBitmap, 512, nh, true);
// ShowSelectedImage.setImageBitmap(BitmapFactory.decodeFile(ImagePath));
ShowSelectedImage.setImageBitmap(FixBitmap);
}
}
This code is work
Is there a way to get the dimensions of the image currently set in the ImageButton? I'm trying to achieve this.
I have a ImageButton with a default pic of 36 x 36. I then select an image of size say 200 x 200. I wanna call something like:
imageButton.setImageBitmap(Bitmap.createScaledBitmap(
bitmap, 36, 36, true));
to shrink the image to 36 x 36. Reason why I want to get the original image size is to cater for hdpi, mdpi and ldpi so I can set dimensions of the bitmap to 36 x 36, 24 x 24 and 18 x 18 respectively before adding it to the ImageButton. Any ideas?
Oh man, I got the answer after randomly fiddling with the code:
imageButton.getDrawable().getBounds().height();
imageButton.getDrawable().getBounds().width();
Try this code -
imageButton.getDrawable().getBounds().height();
imageButton.getDrawable().getBounds().width();
Maurice's answer didn't quite work for me, as I would frequently get 0 back, resulting in an Exception being thrown whenever trying to generate the scaled bitmap:
IllegalArgumentException: width and height must be > 0
I found a few other options if it helps anyone else.
Option 1
The imageButton is a View which means we can get the LayoutParams and take advantage of the built-in height and width properties. I found this from this other SO answer.
imageButton.getLayoutParams().width;
imageButton.getLayoutParams().height;
Option 2
Have our imageButton come from a Class which extends ImageButton, and then override View#onSizeChanged.
Option 3
Get the drawing rectangle on the view and use the width() and height() methods to get the dimensions:
android.graphics.Rect r = new android.graphics.Rect();
imageButton.getDrawingRect(r);
int rectW = r.width();
int rectH = r.height();
Combination
My final code ended up combining the three and selecting the max. I am doing this because I will get different results, depending on which phase the application is in (like when the View has not been fully drawn).
int targetW = imageButton.getDrawable().getBounds().width();
int targetH = imageButton.getDrawable().getBounds().height();
Log.d(TAG, "Calculated the Drawable ImageButton's height and width to be: "+targetH+", "+targetW);
int layoutW = imageButton.getLayoutParams().width;
int layoutH = imageButton.getLayoutParams().height;
Log.e(TAG, "Calculated the ImageButton's layout height and width to be: "+targetH+", "+targetW);
targetW = Math.max(targetW, layoutW);
targetH = Math.max(targetW, layoutH);
android.graphics.Rect r = new android.graphics.Rect();
imageButton.getDrawingRect(r);
int rectW = r.width();
int rectH = r.height();
Log.d(TAG, "Calculated the ImageButton's getDrawingRect to be: "+rectW+", "+rectH);
targetW = Math.max(targetW, rectW);
targetH = Math.max(targetH, rectH);
Log.d(TAG, "Requesting a scaled Bitmap of height and width: "+targetH+", "+targetW);
Bitmap scaledBmp = Bitmap.createScaledBitmap(bitmap, targetW, targetH, true);