Android XML - Have an ImageView fill height while maintaining ANY width ratio - android

I'm building an Android application and am now designing the user profile activity.
It is supposed to display the profile picture at the top in a rectangle container that looks like a banner.
<ImageView
android:id="#+id/user_pic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:background="#android:color/transparent"
android:layout_centerHorizontal="true"
android:elevation="2dp"/>
This XML code works well if the image is a 1x1 ratio or if the height is bigger than the width. (I display a blurry and stretched version of the same image in background so it's not completely ugly).
The problem I hit is when the WIDTH is bigger than the HEIGHT. It tries to scale the height (in my case to 300dp) but then the width hits the borders of the layout and it stops resizing, leaving me with an empty space under the image.
I know that the scaleType="centerCrop" does pretty much what I'm looking for, but if the ratio of the image is 1x1 it's cropping the top part of the image (most of the time the hairs) of the person, which I don't want.
So basically, is there a way in XML, or in JAVA, to resize the height of an image to fill my container while keeping the width ratio and ignoring the max width of the parent layout ? (I imagine it's supposed to crop what would overflow just like "centerCrop" would).
Here is an image of what I'm currently acheiving and the problem occuring. The grey part is a blurry and resized version of the same Image, so it doesn't look too ugly when the image can't fill the width (for example, in a 1x1 ratio picture).
https://i.stack.imgur.com/FUDcP.png

I am not getting you properly, but you want keep the aspect ratio of Image, you should match_parent to android:height and try to wrap_content the width of ImageView. Try to fitCenter as android:scaleType.
Try the code bellow -
<ImageView
android:id="#+id/user_pic"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:background="#android:color/transparent"/>

The answer was found at this link.
Basically, the trick used is to make a custom ImageView class in Java, then use it. It will calculate the correct area to crop, if necessary, depending of the situation.
If anyone finds a pure XML solution, feel free to tell me !
Without any further wait, here is the code I used.
JAVA CLASS
public class FitYCropXImageView extends ImageView {
boolean done = false;
#SuppressWarnings("UnusedDeclaration")
public FitYCropXImageView(Context context) {
super(context);
setScaleType(ScaleType.MATRIX);
}
#SuppressWarnings("UnusedDeclaration")
public FitYCropXImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setScaleType(ScaleType.MATRIX);
}
#SuppressWarnings("UnusedDeclaration")
public FitYCropXImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setScaleType(ScaleType.MATRIX);
}
private final RectF drawableRect = new RectF(0, 0, 0,0);
private final RectF viewRect = new RectF(0, 0, 0,0);
private final Matrix m = new Matrix();
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (done) {
return;//Already fixed drawable scale
}
final Drawable d = getDrawable();
if (d == null) {
return;//No drawable to correct for
}
int viewHeight = getMeasuredHeight();
int viewWidth = getMeasuredWidth();
int drawableWidth = d.getIntrinsicWidth();
int drawableHeight = d.getIntrinsicHeight();
drawableRect.set(0, 0, drawableWidth, drawableHeight);//Represents the original image
//Compute the left and right bounds for the scaled image
float viewHalfWidth = viewWidth / 2;
float scale = (float) viewHeight / (float) drawableHeight;
float scaledWidth = drawableWidth * scale;
float scaledHalfWidth = scaledWidth / 2;
viewRect.set(viewHalfWidth - scaledHalfWidth, 0, viewHalfWidth + scaledHalfWidth, viewHeight);
m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER /* This constant doesn't matter? */);
setImageMatrix(m);
done = true;
requestLayout();
}
}
XML
<com.app.YourAppName.YourAppName.CustomViews.FitYCropXImageView
android:id="#+id/user_pic"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:elevation="2dp"
/>
JAVA ACTIVITY IF NECESSARY
import com.app.YourAppName.YourAppName.CustomViews.FitYCropXImageView;
FitYCropXImageView profilePic = (FitYCropXImageView) findViewById(R.id.user_pic);

If your ImageView width is match_parent and Your Original Image size is 300x400
and also assume that Deice's WIDTH_PIXEL 1080 and HEIGHT_PIXEL 1920
Your ImageView need to be 1080x1440(3:4)
You can use like below.
public static final int WIDTH_PIXEL = Resources.getSystem().getDisplayMetrics().widthPixels;
public static final int HEIGHT_PIXEL = Resources.getSystem().getDisplayMetrics().heightPixels;
//ratioX : ratioY = givenX : X
public static int getRatio(int ratioX, int ratioY, int givenX) {
int result = (int) ((((float) ratioY * (float) givenX) / (float) ratioX) + 0.5f);
//Log.v(TAG, "Request -> " + ratioX + " : " + ratioY + " = " + givenX + " : " + result);
return result;
}
{//Usage
int width = 300;
int height = 400;
int resultHeight = getRatio(width, height , WIDTH_PIXEL);
myImageVIew.getLayoutParams().height = resultHeight ;
}
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
I hope it can be a hint for you.

Related

How to fit png/jpg images on any screen without decreasing quality?

I know it's bit tricky and still thinking whether is it possible or not, but i want to make my image to adjust without decreasing in image quality on any android device when i used vector Drawable it was pretty convenient but sometimes size of vectors are not memory efficient so i don't want to use them.Though i want to know if there is any way to adjust simple PNG or JPEG files irrespective of resolution and screen size in Android?
If anyone can give me way,it would be great help !!
Use Resize Image View (Custom Image View)
public class ResizableImageView extends ImageView {
public ResizableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizableImageView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
// get drawable from imageview
if (d == null) {
super.setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
return;
}
int imageHeight = d.getIntrinsicHeight();
int imageWidth = d.getIntrinsicWidth();
// get height and width of the drawable
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// get width and height extracts the size from the supplied measure specification.
float imageRatio = 0.0F;
if (imageHeight > 0) {
imageRatio = imageWidth / imageHeight;
}
float sizeRatio = 0.0F;
if (heightSize > 0) {
sizeRatio = widthSize / heightSize;
}
int width;
int height;
if (imageRatio >= sizeRatio) {
// set width to maximum allowed
width = widthSize;
// scale height
height = width * imageHeight / imageWidth;
} else {
// set height to maximum allowed
height = heightSize;
// scale width
width = height * imageWidth / imageHeight;
}
setMeasuredDimension(width, height);
// This method must be called to store the measured width and measured height. Failing to do so will trigger an exception at measurement time
}
}

How can I prevent my custom view drawing from being clipped by the view bounds?

I'm attempting to extend ImageView and add a shadow. I'm running into a problem where the shadow is being clipped by the view bounds and looks quite bad:
I've attempted to set the width/height via LayoutParams programmatically as well as trying different XML properties like android:adjustViewBounds, but there is no change in the display. Similarly, setting a android:layout_margin is ineffective in preventing the shadow from being clipped.
Can anyone help me figure out how to avoid this clipping? I'm sure there is something obvious I'm overlooking here.
Update:
My code is very specific at this time to exactly one case: I'm trying to draw a circle "shadow" underneath a circle bitmap. It is obvious that the view bounds are causing the clipping, but I have not been able to find a solution that will allow me to expand the view bounds.
It has been claimed on #android-dev that my math is simply wrong. I am accounting for screen density, which is a common problem. I have triple checked my math on all counts and cannot find where it might be wrong.
Initially, on an xxhdpi screen, density 3.0, the 56dp image is exactly 168px wide and 168px high. After adding 2dp to the width and height to account for the offset, the layoutParams have width=174 and height=174.
My basic approach is to let the super ImageView do its thing and draw the bitmap specified in xml and all I want to do is draw a little something additionally. Is this approach fundamentally flawed?
I use the largest of width or height in onLayout to determine what the radius of my shadow circle should be: radius = Max(width, height) / 2. I draw a circle with this radius and center point at (Cx, Cy) where Cx is the midpoint of the width plus a x-offset and Cy is the midpoint of the height plus a y-offset to create the shadow effect. I draw the additional circle using a canvas to a bitmap and later in onDraw I place my circle on the canvas before allowing the ImageView super onDraw to take care of the source bitmap.
Additionally, in my onLayout I attempt to account for the x- and y-offset distances and add those to my view's width and height via LayoutParams, but no change in the size of the view is evidenced when the view is drawn.
Here is the code that I'm using: https://gitlab.com/dm78/ImageViewWithShadowExample/
Here is the relevant code:
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<dm78.example.imageviewwithshadowexample.CustomShadowImageView
android:id="#+id/circle"
android:layout_gravity="center"
android:src="#drawable/circle"
android:layout_margin="16dp"
android:layout_width="56dp"
android:layout_height="56dp"/>
</FrameLayout>
CustomShadowImageView.java
package dm78.example.imageviewwithshadowexample;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class CustomShadowImageView extends ImageView {
public static final String TAG = CustomShadowImageView.class.getSimpleName();
public static final float SHADOW_RADIUS_DP = 3f;
public static final float SHADOW_X_OFFSET_DP = 2f;
public static final float SHADOW_Y_OFFSET_DP = 2f;
private Paint mPaint;
private float mShadowRadius;
private float radius;
private float cx;
private float cy;
private float mShadowXOffset;
private float mShadowYOffset;
private Bitmap mShadowBitmap;
private FrameLayout.LayoutParams layoutParams;
private boolean expanded;
public CustomShadowImageView(Context context) {
super(context);
init();
}
public CustomShadowImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomShadowImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
Log.d(TAG, "init " + this.hashCode());
DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
mShadowRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_RADIUS_DP, dm);
mShadowXOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_X_OFFSET_DP, dm);
mShadowYOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SHADOW_Y_OFFSET_DP, dm);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//noinspection deprecation
int shadowColor = getContext().getResources().getColor(R.color.shadow);
mPaint.setColor(shadowColor);
expanded = false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d(TAG, String.format("onMeasure %d w: %d, h: %d", this.hashCode(), MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
Log.d(TAG, String.format("onLayout %d changed: %b, l: %d, t: %d, r: %d, b: %d", this.hashCode(), changed, left, top, right, bottom));
super.onLayout(changed, left, top, right, bottom);
if (changed) {
if (!expanded) {
layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
layoutParams.width = (int) (layoutParams.width + mShadowXOffset);
layoutParams.height = (int) (layoutParams.height + mShadowYOffset);
expanded = true;
}
cx = (right - left) / 2 + mShadowXOffset;
cy = (bottom - top) / 2 + mShadowYOffset;
boolean widthGreater = (right - left) > (bottom - top);
radius = (widthGreater ? right - left : bottom - top) / 2;
if (mShadowBitmap == null) {
Bitmap bitmap = Bitmap.createBitmap(right - left, bottom - top, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawCircle(cx, cy, radius, mPaint);
if (Build.VERSION.SDK_INT >= 17 && !isInEditMode()) {
RenderScript rs = RenderScript.create(getContext());
Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
Allocation output = Allocation.createTyped(rs, input.getType());
ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
script.setRadius(mShadowRadius);
script.setInput(input);
script.forEach(output);
output.copyTo(bitmap);
}
mShadowBitmap = bitmap;
}
}
}
#Override
protected void onDraw(Canvas canvas) {
Log.d(TAG, "onDraw " + this.hashCode());
canvas.drawBitmap(mShadowBitmap, mShadowXOffset, mShadowYOffset, null);
super.onDraw(canvas);
}
}
By default, Views are only allowed by their parent to draw within their bounds and not beyond.
You have two options:
Either you add some padding to your Imageview to enlarge its bounds
instead of using layout_margin and you draw within these bounds.
Either you disable the child clipping behavior by setting android:clipChildren="false" to your FrameLayout.
If the cause is the view parent's padding, add the following line to the parent xml element:
android:clipToPadding="false"
android:clipChildren="false" in the parent layout worked for me

ListView with images with different height coming from network causes "flickers"

Here's the scenario: I've some info to show on a ListView. Every row can have, title, body, date, avatar, and more data. And a ImageView that comes from network. The ImageView has a different height on every row. I know what will be it's height in px once downloaded.
What I'm doing right now is to adjust the width of the image to fill it's parent, and auto adjusting the it's height automatically.
I'm loading a fixed placeholder while the image is downloaded.
Here's the code of the ImageView:
public class ResizableImageView2 extends ImageView {
public ResizableImageView2(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
Drawable d = getDrawable();
if(d!=null){
// ceil not round - avoid thin vertical gaps along the left/right edges
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
setMeasuredDimension(width, height);
}else{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
The problem is: The placeholder is loaded with a fixed size, and once downloaded we put the ImageView, the layout recalculates it self, and that causes wasted cpu, slow scrolling.
I'm already pre-caching the incoming rows to "minimize" this problem. This way the app loads an image from disk/memory 80% of the time (if you are in a good network and you scroll in a normal speed) and the listview does not "flicker".
The solution I'm searching is to preset the size of the placeholder at the same size that will be the downloaded image. But for some reason I'm having a hard time doing this.
I can make a little cropping of the image (some small pixels) if needed, but nothing as make all the images croped at the same size :P
Ideas? examples?
Finally for me the solution was:
public class ResizableImageView2 extends ImageView {
private int fixedHeight = -1;
private int fixedWidth = -1;
public ResizableImageView2(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (BuildConfig.DEBUG) Log.e("ResizableImageView2", "onMeasure called!");
if (fixedHeight != -1 && fixedWidth != -1) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) Math.ceil((float) width * (float) fixedHeight / (float) fixedWidth);
setMeasuredDimension(width, height);
} else {
Drawable d = getDrawable();
if (d != null) {
// ceil not round - avoid thin vertical gaps along the left/right edges
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
setMeasuredDimension(width, height);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
public void setFixedHeight(int fixedHeight) {
this.fixedHeight = fixedHeight;
}
public void setFixedWidth(int fixedWidth) {
this.fixedWidth = fixedWidth;
}
I can set the height and the width before downloading the bitmap with setFixedHeight and setFixedWidth. The place holder should be a 9 patch for better stretching. and at the layout I've android:scaleType="fitXY".
With this I can pre-set the size of the imageview before downloading it, and once downloaded the image view will fill the width and have a height with the correct aspect ratio.

ImageView adjustViewBounds not working

I have an ImageView with android:layout_width=100dp, android:layout_height=wrap_content and android:adjustViewBounds=true
It's source is a 50 x 50 px picture. But the aspect ratio is not preserved - height of the ImageView is 50px, not 100px (i.e. adjustViewBounds is not working). If I have a 200x200px picture it works - width and height are 100px. This code results in a 100px wide and 50px tall picture but the src image is square:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/photo"
android:src="#drawable/icon"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:adjustViewBounds="true" />
</LinearLayout>
The issue is that adjustViewBounds will not increase the size of the ImageView beyond the natural dimensions of the drawable. It will only shrink the view to maintain aspect ratio; if you provide a 500x500 image instead of a 50x50 image, this should work.
If you're interested in the spot where this behavior is implemented, see ImageView.java's onMeasure implementation.
One workaround is to implement a custom ImageView that changes this behavior in onMeasure.
There's a more simple way. Make your ImageView like this:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:adjustViewBounds="true"/>
This way drawable will stretch to fit in the ImageView center by preserving
the aspect ratio. We just have to calculate the right height to make it proportional
so we don't have any blank space:
private void setImageBitmap(Bitmap bitmap, ImageView imageView){
float i = ((float)imageWidth)/((float)bitmap.getWidth());
float imageHeight = i * (bitmap.getHeight());
imageView.setLayoutParams(new RelativeLayout.LayoutParams(imageWidth, (int) imageHeight));
imageView.setImageBitmap(bitmap);
}
In addition to #RomanNurik's answer
You can find working solution here, either copy-paste code or just add the Gradle dependency
dependencies {
compile 'com.inthecheesefactory.thecheeselibrary:adjustable-imageview:1.0.1'
}
P.S. Solution provided by #Nilzor didn't work for me
I had a similar requirement; in my case, I wanted the image to be square, and wanted the ImageView to match the aspect ratio so I could use its background and padding to draw a border.
I read the answers here but instead of overriding ImageView, I decided to make a Layout that guarantees its contents (should be only one view) are square. That way I could use a standard ImageView inside it. (And you never know, I might want to make something else square later. Although probably not.)
In case it's useful for anyone else, here's the code (feel free to copy). There are probably bugs as I just made it work for my app then stopped. :)
public class SquareLayout extends ViewGroup
{
public SquareLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public SquareLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SquareLayout(Context context)
{
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
// Work out width and height, and square size.
int width = r - l;
int height = b - t;
int size, xOffset, yOffset;
if(width < height)
{
size = width;
xOffset = 0;
yOffset = (height - size) / 2;
}
else
{
size = height;
xOffset = (width - size) / 2;
yOffset = 0;
}
for(int i = 0; i < getChildCount(); i++)
{
View child = getChildAt(i);
child.layout(xOffset, yOffset, size + xOffset, size + yOffset);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// Get width and height.
int w = -1, h = -1;
switch(MeasureSpec.getMode(widthMeasureSpec))
{
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
w = MeasureSpec.getSize(widthMeasureSpec);
break;
case MeasureSpec.UNSPECIFIED:
break;
}
switch(MeasureSpec.getMode(heightMeasureSpec))
{
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
h = MeasureSpec.getSize(heightMeasureSpec);
break;
case MeasureSpec.UNSPECIFIED:
break;
}
// If only one of width/height is unspecified, set them both the same.
if(w == -1 && h != -1)
{
w = h;
}
else if(h == -1 && w != -1)
{
h = w;
}
// Either they're both specified or both unspecified.
int childMeasureSpec;
if(w == -1)
{
childMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
else
{
childMeasureSpec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
}
// Pass through to children.
int maxDimension = 1;
for(int i = 0; i < getChildCount(); i++)
{
View child = getChildAt(i);
child.measure(childMeasureSpec, childMeasureSpec);
maxDimension = Math.max(maxDimension, child.getMeasuredWidth());
maxDimension = Math.max(maxDimension, child.getMeasuredHeight());
}
if(w == -1)
{
w = maxDimension;
h = maxDimension;
}
setMeasuredDimension(w, h);
}
}
this line will do it for you android:scaleType="fitXY"

Trying to get the display size of an image in an ImageView

I'm trying to get the real size of an image displayed in an image view. Actually my image is larger than the screen and the imageview is resizing the image to diplay it. I'm looking for this new size.
I've tried to override the onDraw method of the ImageView in a custom view but I'm not getting the correct height and width...
public class LandImageView extends ImageView
{
public LandImageView( Context context )
{
super( context );
}
public LandImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public LandImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onDraw( Canvas canvas )
{
super.onDraw( canvas );
int test = this.getWidth();
int test2 = this.getHeight();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
}
}
Do you have any clues ?
None of the answers here actually answer the question:
From a Bitmap of any size displayed by an ImageView, find the actual dimensions of the displayed image as opposed to the dimensions of the supplied Bitmap.
Namely:
Using ImageView.getDrawable().getInstrinsicWidth() and getIntrinsicHeight() will both return the original dimensions.
Getting the Drawable through ImageView.getDrawable() and casting it to a BitmapDrawable, then using BitmapDrawable.getBitmap().getWidth() and getHeight() also returns the original image and its dimensions.
The only way to get the actual dimensions of the displayed image is by extracting and using the transformation Matrix used to display the image as it is shown. This must be done after the measuring stage and the example here shows it called in an Override of onMeasure() for a custom ImageView:
public class SizeAwareImageView extends ImageView {
public SizeAwareImageView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Get image matrix values and place them in an array
float[] f = new float[9];
getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
Log.e("DBG", "["+origW+","+origH+"] -> ["+actW+","+actH+"] & scales: x="+scaleX+" y="+scaleY);
}
}
Note: To get the image transformation Matrix from code in general (like in an Activity), the function is ImageView.getImageMatrix() - e.g. myImageView.getImageMatrix()
I extended B T's answer to produce a static method from it, and to include image left and top positions into the ImageView :
/**
* Returns the bitmap position inside an imageView.
* #param imageView source ImageView
* #return 0: left, 1: top, 2: width, 3: height
*/
public static int[] getBitmapPositionInsideImageView(ImageView imageView) {
int[] ret = new int[4];
if (imageView == null || imageView.getDrawable() == null)
return ret;
// Get image dimensions
// Get image matrix values and place them in an array
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView.getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
ret[2] = actW;
ret[3] = actH;
// Get image position
// We assume that the image is centered into ImageView
int imgViewW = imageView.getWidth();
int imgViewH = imageView.getHeight();
int top = (int) (imgViewH - actH)/2;
int left = (int) (imgViewW - actW)/2;
ret[0] = left;
ret[1] = top;
return ret;
}
I found that WarrenFaith's suggestion of using setAdjustViewBounds worked, but I had to change the ImageView's layout_width/layout_height to 'wrap_content' (with 'match_parent', setAdjustViewBounds did nothing). To get the height/width/gravity behaviour I wanted, I had to wrap the ImageView in a FrameLayout:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
/>
</FrameLayout>
After doing this, the ImageView's dimensions (as returned by getWidth() and getHeight()) matched the display size of the image.
Try
ImageView.getDrawable().getIntrinsicHeight()
ImageView.getDrawable().getIntrinsicWidth()
I'm just passing by but hope it still helps
I'm going under the assumption your talking about bitmaps in your imageView
what you want to understand is the difference between
bmpBack.getWidth()
-> this gives you the size of your bitmap
and
bmpBack.getScaledWidth(canvas);
-> this will give you the size of the bitmap as displayed on the screen.
I never used ImageView because the relative display was driving me mad so in the end I just override the onDraw and did my canvas very similarly to opengl.
I think this is your problem
cheers
Jason
You can use imageview's viewtreeobserver and addonDrawListener.
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
float[] f = new float[9];
imageView.getImageMatrix().getValues(f);
// Extract the scale values using the constants (if aspect ratio maintained, scaleX == scaleY)
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
// Get the drawable (could also get the bitmap behind the drawable and getWidth/getHeight)
final Drawable d = imageView. getDrawable();
final int origW = d.getIntrinsicWidth();
final int origH = d.getIntrinsicHeight();
// Calculate the actual dimensions
final int actW = Math.round(origW * scaleX);
final int actH = Math.round(origH * scaleY);
}
});
try overriding onMeasure(int widthMeasureSpec, int heightMeasureSpec) instead of onSizeChanged.
If I get you correctly you need your ImageView dimension, in order to scale your image accordingly. I did this with a custom class, where I override the onMeasure() call to get width and height.
class LandImageView extends ImageView{
public LandImageView (Context context, AttributeSet attrs) {
super (context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
Log.v("", String.format("w %d h %d", width, height));
// width and height are the dimensions for your image
// you should remember the values to scale your image later on
this.setMeasuredDimension(width, height );
}}
In onMeasure you get the width and height for your image so that it fits your view.
You can use the LandImageView in your Layout like this:
<my.package.LandImageView ... >
public class images extends Activity {
ImageView i1;
LinearLayout l1;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
i1=(ImageView)findViewById(R.id.iv);
l1 = new LinearLayout(this);
ImageView i = new ImageView(this);
ImageView i1 = new ImageView(this);
i.setImageResource(R.drawable. imagename.........);
l1.addView(i);
l1.addView(i1);
setContentView(l1);
}
}
first add the images in ur resource folder....... and in xml file create a image view....
I was looking for a solution to set the dimension of the image view to the scaled image to prevent empty space on top/bottom or left/right (cause the dimension of the view doesn't changed to fit the scaled image).
What I found to do the trick was using the method mImageView.setAdjustViewBounds(true); which results in the correct layout dimension. I don't have the scaled image dimension but I got the result I was looking for... just if someone needs it...
Try loading your resource using BitmapFactory.decodeResource (Resources res, int id, BitmapFactory.Options opts) with the BitmapFactory.Options() class. BitmapFactory.Options() has a flag which called "inJustDecodeBounds" and gives only the resource dimensions.
Hope that helped.
I thinks you need the size of the image visible int the screen, for that you simply need to do this:
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
initialWidth = this.getMeasuredWidth();
initialHeight = this.getMeasuredHeight();
}
And do take a look at the documentation of the methods like: getWidht(), getHeight(), getMeasuredWidth(), getMeasuredHeight(), etc. You will get to know what it does to give you that size.
EDIT: If you want actual width and height of the image being loaded into the imageView. You may want to change the method calls from getMeasuredWidth() to getIntrinsicWidth() and getMeasuredHeight() to getIntrinsicHeight() like this:
Drawable drawable = getDrawable();
actualWidth = drawable.getIntrinsicWidth();
actualHeight = drawable.getIntrinsicHeight();
Simply can be used this code (in activity):
#Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
final ImageView imageView = findViewById(R.id.imageView);
int width = imageView.getWidth(), height = imageView.getHeight();
}
In case the picture reaches the ends of the imageView.
How about ImageView.getBackground() or ImageView.getDrawable(), then Drawable.getBounds()?

Categories

Resources