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()
Here's what I need:
I have a Surface view that has a square (image view) on top of it. I need to capture an image, and crop out the area that was visible only within the square.
This code gives me decent results but specific only to some devices:
int width=(int)(bitmap.getWidth()*60/100);
int height=(bitmap.getHeight()*100/100); //dont change
bitmap=Bitmap.createBitmap(bitmap,150,0, width-55, height);
Is there any way I could generalize this code? Is there any other way to get what I need?
EDIT: This is how I got it to work-
Save the image from the surface view as a bitmap (This is very simple. There are many examples available on the internet that show how to do that)
Use this code in a function, and call it after the image is clicked
//bitmap is the object where the image is stored
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int left;
if(width > height){
left = (width - height)/2;
}
else {
left = (height - width)/2;
}
bitmap=Bitmap.createBitmap(bitmap,left,0, height, height);
I have a GridView. The data of GridView is request from a server.
Here is the item layout in GridView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/analysis_micon_bg"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="#dimen/half_activity_vertical_margin"
android:paddingLeft="#dimen/half_activity_horizontal_margin"
android:paddingRight="#dimen/half_activity_horizontal_margin"
android:paddingTop="#dimen/half_activity_vertical_margin" >
<ImageView
android:id="#+id/ranking_prod_pic"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="#string/app_name"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/ranking_rank_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/ranking_prod_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/ranking_prod_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
I request data from server, get image url and load image to Bitmap
public static Bitmap loadBitmapFromInputStream(InputStream is) {
return BitmapFactory.decodeStream(is);
}
public static Bitmap loadBitmapFromHttpUrl(String url) {
try {
return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return null;
}
}
and there is the code of getView(int position, View convertView, ViewGroup parent) method in adapter
Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);
The image size is 210*210. I run my application on my Nexus 4. The image does fill ImageView width, but the ImageView height does not scale. ImageView does not show the whole image.
How do I solve this problem?
Without using any custom classes or libraries:
<ImageView
android:id="#id/img"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
scaleType="fitCenter" (default when omitted)
will make it as wide as the parent allows and up/down-scale as needed keeping aspect ratio.
scaleType="centerInside"
if the intrinsic width of src is smaller than parent widthwill center the image horizontally
if the intrinsic width of src is larger than parent widthwill make it as wide as the parent allows and down-scale keeping aspect ratio.
It doesn't matter if you use android:src or ImageView.setImage* methods and the key is probably the adjustViewBounds.
I like answer of arnefm but he made a small mistake (see comments) which I will try to correct:
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* ImageView that keeps aspect ratio when scaled
*/
public class ScaleImageView extends ImageView {
public ScaleImageView(Context context) {
super(context);
}
public ScaleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScaleImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
Drawable drawable = getDrawable();
if (drawable == null) {
setMeasuredDimension(0, 0);
} else {
int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
if (measuredHeight == 0 && measuredWidth == 0) { //Height and width set to wrap_content
setMeasuredDimension(measuredWidth, measuredHeight);
} else if (measuredHeight == 0) { //Height set to wrap_content
int width = measuredWidth;
int height = width * drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
setMeasuredDimension(width, height);
} else if (measuredWidth == 0){ //Width set to wrap_content
int height = measuredHeight;
int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
setMeasuredDimension(width, height);
} else { //Width and height are explicitly set (either to match_parent or to exact value)
setMeasuredDimension(measuredWidth, measuredHeight);
}
}
} catch (Exception e) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Thus your ImageView will be scaled properly and will have no dimension problems if (for instance) put inside of ScrollView
I had a similar problem once. I solved it by making a custom ImageView.
public class CustomImageView extends ImageView
Then override the onMeasure method of the imageview. I did something like this I believe:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
Drawable drawable = getDrawable();
if (drawable == null) {
setMeasuredDimension(0, 0);
} else {
float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
if (imageSideRatio >= viewSideRatio) {
// Image is wider than the display (ratio)
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int)(width / imageSideRatio);
setMeasuredDimension(width, height);
} else {
// Image is taller than the display (ratio)
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = (int)(height * imageSideRatio);
setMeasuredDimension(width, height);
}
}
} catch (Exception e) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
This will stretch the image to fit the screen while maintaining the aspect ratio.
Use android:scaleType="centerCrop".
FOR IMAGE VIEW (set these parameters)
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:scaleType = "fitCenter"
android:adjustViewBounds = "true"
Now whatever the size of the image is there, it's width will match the parent and height will be according to match the ratio. I have tested this and I am 100% sure.
we want this -->
not this -->
// Results will be:
Image width -> stretched as match parent
Image height -> according to image width (maximum to aspect ratio)
// like the first one
I did something similar to the above and then banged my head against the wall for a few hours because it did not work inside a RelativeLayout. I ended up with the following code:
package com.example;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class ScaledImageView extends ImageView {
public ScaledImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final Drawable d = getDrawable();
if (d != null) {
int width;
int height;
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
height = MeasureSpec.getSize(heightMeasureSpec);
width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
} else {
width = MeasureSpec.getSize(widthMeasureSpec);
height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
}
setMeasuredDimension(width, height);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
And then to prevent RelativeLayout from ignoring the measured dimension I did this:
<FrameLayout
android:id="#+id/image_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/something">
<com.example.ScaledImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="150dp"/>
</FrameLayout>
This will not be applicable if you set image as background in ImageView, need to set at src(android:src).
Thanks.
Yo don't need any java code. You just have to :
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
The key is in the match parent for width and height
To create an image with width equals screen width, and height proportionally set according to aspect ratio, do the following.
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
// creating the image that maintain aspect ratio with width of image is set to screenwidth.
int width = imageView.getMeasuredWidth();
int diw = resource.getWidth();
if (diw > 0) {
int height = 0;
height = width * resource.getHeight() / diw;
resource = Bitmap.createScaledBitmap(resource, width, height, false);
}
imageView.setImageBitmap(resource);
}
});
Hope this helps.
Use these properties in ImageView to keep aspect ratio:
android:adjustViewBounds="true"
android:scaleType="fitXY"
You can try to do what you're doing by manually loading the images, but I would very very strongly recommend taking a look at Universal Image Loader.
I recently integrated it into my project and I have to say its fantastic. Does all the worrying about making things asynchronous, resizing, caching images for you. It's really easy to integrate and set up. Within 5 minutes you can probably get it doing what you want.
Example code:
//ImageLoader config
DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();
if (ImageLoader.getInstance().isInited()) {
ImageLoader.getInstance().destroy();
}
ImageLoader.getInstance().init(config);
imageLoadingListener = new ImageLoadingListener() {
#Override
public void onLoadingStarted(String s, View view) {
}
#Override
public void onLoadingFailed(String s, View view, FailReason failReason) {
ImageView imageView = (ImageView) view;
imageView.setImageResource(R.drawable.android);
Log.i("Failed to Load " + s, failReason.toString());
}
#Override
public void onLoadingComplete(String s, View view, Bitmap bitmap) {
}
#Override
public void onLoadingCancelled(String s, View view) {
}
};
//Imageloader usage
ImageView imageView = new ImageView(getApplicationContext());
if (orientation == 1) {
imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
} else {
imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
}
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);
This can lazy load the images, fit them correctly to the size of the imageView showing a placeholder image while it loads, and showing a default icon if loading fails and caching the resources.
-- I should also add that this current config keeps the image aspect ratio, hence applicable to your original question
Try this: it solved the problem for me
android:adjustViewBounds="true"
android:scaleType="fitXY"
Just use UniversalImageLoader and set
DisplayImageOptions.Builder()
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
.build();
and no scale settings on ImageView
try with this simple line... add this line in your xml code in image view tag with out adding any dependency
android:scaleType="fitXY"
use android:ScaleType="fitXY" im ImageView xml
I had a similar issue, I found the reason for this is because you need to calculate the dp. Android studio is calculating the ImageView when you load it from the drawable, but when you are using another method, like loading from bitmap the dp is not automatically accounted for,
Here is my xml
<ImageView
android:id="#+id/imageViewer"
android:layout_width="match_parent"
android:layout_height="match_parent"//dp is not automaticly updated, when loading from a other source
android:scaleType="fitCenter"
tools:srcCompat="#drawable/a8" />
I'm using Kotlin, and loading drawable from an asset file, here's how I calculate this
val d = Drawable.createFromStream(assets.open("imageData/${imageName}.png"), null)
bitHeight = d.minimumHeight//get the image height
imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
imageViewer.setImageDrawable(d)//set the image from the drawable
imageViewer.requestLayout()//here I apply it to the layout
My activity has a background image 1280x800 pixels. I set it using android:scaleType="centerCrop".
There's a flagstaff depicted on a background image and I need to position another image ("flag") above the flagstaff.
If device's screen dimension was exactly 1280x800, then "flag"'s position would be (850, 520). But screen size can vary and Android scales and shifts the background image accordingly to centerCrop flag. Hence I need to assign somehow scale and shift to "flag" image to make it placed nicely above the flagstaff.
I have examined ImageView.java and found that scaleType is used to set a private Matrix mDrawMatrix. But I have no read access to this field as it's private.
So, given
#Override
public void onGlobalLayout()
{
ImageView bg = ...;
ImageView flag = ...;
int bgImageWidth = 1280;
int bgImageHeight = 800;
int flagPosX = 850;
int flagPosY = 520;
// What should I do here to place flag nicely?
}
You can see the size of the screen (context.getResources().getDisplayMetrics().widthPixels, context.getResources().getDisplayMetrics().heightPixels;) and calculate what is visible from the image, for example you could do something like this (haven't really tested it but you should get the idea):
private void placeFlag(Context context) {
ImageView bg = new ImageView(context);
ImageView flag = new ImageView(context);
int bgImageWidth = 1280;
int bgImageHeight = 800;
int flagPosX = 850;
int flagPosY = 520;
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
//calculate the proportions between the width of the bg and the screen
double widthScale = (double) bgImageWidth / (double) screenWidth;
double heightScale = (double) bgImageHeight / (double) screenHeight;
//see the real scale used, it will be the maximum between the 2 values because you are using crop
double realScale = Math.max(widthScale, heightScale);
//calculate the position for the flag
int flagRealX = (int) (flagPosX * realScale);
int flagRealY = (int) (flagPosY * realScale);
}
Also, you should be doing that in the method onGlobalLayout, you could do this in onCreate() or inside the constructor if you want a custom view.
you can use a LayerDrawable approach here to make one drawable image in it's static (In which you can set background image and icon on top of background image in custom_drawable.xml and can use that file as single drawable in activity).For reference go to android developer. Otherwise
For scale issue according to different device resolution put images in different drawable folder and can also design layout different .
I used an background image on canvas and try to re-size it accordingly when the device orientation changed, can anyone suggest me is this possible in android to re-size the canvas dimension on orientation change.
I think it is not possible to resize the canvas.If you are using a background image on the canvas that covers the entire screen, then what you can do is scale this image when the orientation changes in the following way :
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// orientation change
if (_width == width && _height == height) {
_orient = 0;//portrait
this.width = width;
this.height = height;
this._holder=holder;
this.bgBitmap =Bitmap.createScaledBitmap(BitmapFactory
.decodeResource(res, R.drawable.bg), (_holder
.getSurfaceFrame().width()), _holder.getSurfaceFrame()
.height(), false);
} else {
_orient = 1;//landscape
this.width = width;
this.height = height;
his.bgBitmap =Bitmap.createScaledBitmap(BitmapFactory
.decodeResource(res, R.drawable.bg), (_holder
.getSurfaceFrame().width()), _holder.getSurfaceFrame()
.height(), false);
}
super.onSurfaceChanged(holder, format, width, height);
}
This method is part of the class that extends Engine in my code.My code is very lengthy so i'm posting only the relevant part here.