Can I take a view that has been rendered by the Android framework and rescale it to some other size?
You need API 11 or above to scale a view. Here is how:
float scalingFactor = 0.5f; // scale down to half the size
view.setScaleX(scalingFactor);
view.setScaleY(scalingFactor);
In your XML
android:scaleX="0.5"
android:scaleY="0.5"
For scale, I resize the width and height of the view to make it affect the area and position another view.
If you don't want it affect the area and position, use answer of #Gadzair and #Taiti
private void resize(View view, float scaleX, float scaleY) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = (int) (view.getWidth() * scaleX);
layoutParams.height = (int) (view.getHeight() * scaleY);
view.setLayoutParams(layoutParams);
}
Example using
resize(view, 0.5f, 0.8f);
Result
Demo
Related
I have used this PhotoView library for custom ImageView. I want to scale the image at particular point. Here is the method I found is setScale(float scale, float focalX, float focalY, boolean animate)
I am wondering what can I pass a value of focalX and focalY , I have X and Y coordinate which I am passing currently and it scales to very different position.
Here is a snippet,
intResultX = intTotalX / intArraySize;
intResultY = intTotalY / intArraySize;
mMap.setScale(5, intResultX, intResultY, true);
To zoom at particular XY coordinate in Imageview you can pass a value of focalX and focalY along with scale (must be between max scale an min scale of PhotoView) and boolean value to set animation.
Code to get max-min scales:
mPhotoView.getMinimumScale();
mPhotoView.getMaximumScale();
focalX and focalY It can be any points on screen, here I have taken two examples one is center of the screen and other is top-left corner. following is the code for both cases.
Code:
Random r = new Random();
float minScale = mPhotoView.getMinimumScale();
float maxScale = mPhotoView.getMaximumScale();
float randomScale = minScale + (r.nextFloat() * (maxScale - minScale));
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
int centerX=width/2;
int centerY =height/2;
/*pass a value of focalX and focalY to scale image to center*/
//mPhotoView.setScale(randomScale, centerX, centerY, true);
/*pass a value of focalX and focalY to scale image to top left corner*/
mPhotoView.setScale(randomScale, 0, 0, true);
Set zoom to the specified scale. Image will be centered around the point
(focusX, focusY). These floats range from 0 to 1 and denote the focus point
as a fraction from the left and top of the view. For example, the top left
corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
/*setZoom can be called before the image is on the screen, but at this point,
image and view sizes have not yet been calculated in onMeasure. Thus, we should
delay calling setZoom until the view has been measured.*/
if (!onDrawReady) {
delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
return;
}
if (scaleType != mScaleType) {
setScaleType(scaleType);
}
resetZoom();
scaleImage(scale, viewWidth / 2, viewHeight / 2, true);
matrix.getValues(m);
m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
matrix.setValues(m);
fixTrans();
setImageMatrix(matrix);
}
Hope this helps. Happy coding.
I have game field which is a Group with Actors. The Group locketed in Table wich is locketed in ScrollPane. I have two Buttons to zoom in and zoom out the game field. Here is my code how I do it:
TextButton zoomInBtn = new TextButton("+", menuBtnStyle);
zoomInBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width + width * 0.1f;
if (newWidth > myWorld.getMaxWidth()) {
newWidth = myWorld.getMaxWidth();
}
float newHeight = height * newWidth / width;
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
TextButton zoomOutBtn = new TextButton("-", menuBtnStyle);
zoomOutBtn.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
float width = fieldGroup.getWidth();
float height = fieldGroup.getHeight();
float newWidth = width - width * 0.1f;
if (newWidth < myWorld.getMinWidth()) {
newWidth = myWorld.getMinWidth();
}
float newHeight = height * newWidth / width;
Actor widget = scrollPane.getWidget();
fieldGroup.setWidth(newWidth);
fieldGroup.setHeight(newHeight);
myWorld.setWidth(Math.round(newWidth));
fieldGroup.reinitialiseChildren();
Cell cell = fieldTable.getCell(fieldGroup);
cell.clearActor();
cell.setActor(fieldGroup);
}
});
I change the size of my fieldGroup with Image Actors in it. And then readding it to Table.
The problem is: when I zoom with buttons it always zoom around left corner. I want it to zoom from canter of ScrollPane. I know that I can do it with Ortographic Camera, but it would be difficult, I think, to make it movements so smooth as ScrollPane. So maybe there is some way to do it with ScrollPane.
Every time you zoom in or out you would have to change the x and y position of the group relative to the zoom. You can do this using the setScrollx (and y) of your scroll pane.
You need to set so that the middle of the part you are viewing stays in the middle of the scrollpane. You can work out middle of newWidth by dividing it by 2. If the scrollPane is fullScreen you can half the value obtained by Gdx.graphics.getwidth();. The difference between these two is then the value which will keep the middles aligned.
newXvalue =(newWidth/2) - ((Gdx.graphics.getWidth())/2);
scrollPane.setScrollx(newXvalue);
The same should be done for y.
Note: if you want to zoom and move at same time, this will not work, I would recommend using orthographic camera with a gesture listener for that.
Given a simple RelativeLayout like this:
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#0fffff">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="#drawable/img001" />
</RelativeLayout>
the left/top spacing between the layout border and the image border depends on the W/H ratio of the image being load in the imageView.
How can I know (programmatically) the real margin (width and height of the cyan area) after an image is shown in this layout?
This method will calculate the new rectangle which bounds the object after FIT_CENTER and all other related values.
It should work on all cases of object and container.
public static Rect calculateFitCenterObjectRect(float containerWidth, float containerHeight, float objectWidth, float objectHeight) {
// scale value to make fit center
double scale = Math.min( (double)containerWidth / (double)objectWidth, (double)containerHeight / (double)objectHeight);
int h = (int) (scale * objectHeight); // new height of the object
int w = (int) (scale * objectWidth); // new width of the object
int x = (int) ((containerWidth - w) * 0.5f); // new x location of the object relative to the container
int y = (int) ((containerHeight - h) * 0.5f); // new y location of the object relative to the container
return new Rect(x, y, x + w, y + h);
}
You can use FrameLayout to position the view wherever you want after using the previous method with the new x, y, width, height of the scaled object.
If you know the width of the ImageView, like this
int ivWidth = iv.getMeasuredWidth();
and the total width of the layout (your RelativeLayout), like this
int layoutWidth = yourLayout.getWidth();
then, you can easily get the horizontal margin, like this
int horizontalMargin = (layoutWidth - ivWidth)/2;
And the same goes for height.
You should call functions like getWidth and getHeight after the dimensions of your layout have been calculated, as described by Veer's and Khan's answer on How to get the width and height of an Image View in android?.
Calling getWidth or getHeight in onCreate will return 0.
I am designing an app that must look good on all android devices. In an activity i want to set the background. The image i want to use has an important figure in the right bottom corner
What I want:
- maintain aspect ratio
- right bottom corner of original image must be visible
- fullscreen
- must work on portrait and landscape
I have tried all the scaletype options, the fit options don't fill the entire screen and the centercrop crops at all sides (so it slices of a part of the right bottom corner).
First make an imageView for your drawable and customize it by changing <ImageView> to <com.packagename.CenterCropShiftsUp>, and set the scaleType to centerCrop.
Create CenterCropShiftsUp.java in the package I just mentioned, and use this code to shift the drawable upwards:
package nl.mijnverzekering.views;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CenterCropShiftsUp extends ImageView
{
public CenterCropShiftsUp(Context context, AttributeSet attrs)
{
super(context, attrs);
}
#Override
protected boolean setFrame(int l, int t, int r, int b)
{
int drawableWidth = getDrawable().getIntrinsicWidth();
int drawableHeight = getDrawable().getIntrinsicHeight();
int viewWidth = r - getPaddingLeft() - getPaddingRight();
int viewHeight = b - getPaddingTop() - getPaddingBottom();
float heightRatio = 1 / ((float) drawableHeight / (float) viewHeight);
float widthRatio = 1 / ((float) drawableWidth / (float) viewWidth);
// Choose the biggest ratio as scaleFactor
// (centerCrop does the same: the drawable never scales down to leave part of the screen empty)
float scale = heightRatio > widthRatio ? heightRatio : widthRatio;
int newDrawableHeight = (int) (scale * (float) drawableHeight);
// Shifts the t (top) of the imageFrame up (t -=)
// This calculation aligns the bottom of the drawable to the bottom of the screen
t -= (newDrawableHeight - b);
return super.setFrame(l, t, r, b);
}
}
It first calculates the scaleFactor of the image, then uses this scale to calculate the new drawableHeight (just as centerCrop would do it). With this height you can calculate how far the frame of the ImageView should be shifted upwards (using setFrame() to get the bottom of the drawable aligned to the bottom of the screen).
The alignment on the right, which you also requested, is of course automatically fixed due to the properties of centerCrop itself.
It seems to be a litte bit late, nevertheless I want to post my answer. I needed to have a top left shifted view, whereas the width is always cropped. I found this library (https://github.com/cesards/CropImageView), but I decided to just use portions of it. It ended up in overriding setFrameand setting the scale type to Matrix in the constructor of my custom image view.
#Override
protected boolean setFrame(int l, int t, int r, int b) {
boolean changed = super.setFrame(l, t, r, b);
int viewWidth = r - getPaddingLeft() - getPaddingRight();
int viewHeight = b - getPaddingTop() - getPaddingBottom();
if (viewHeight > 0 && viewWidth > 0) {
final Matrix matrixCopy = new Matrix();
matrixCopy.set(getImageMatrix());
final Drawable drawable = getDrawable();
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
float scaleY = (float) viewHeight / (float) drawableHeight;
float scaleX = (float) viewWidth / (float) drawableWidth;
float scale = scaleX > scaleY ? scaleX : scaleY;
matrixCopy.setScale(scale, scale);
setImageMatrix(matrixCopy);
}
return changed;
}
The code is simple:
<ImageView android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/cat"/>
Notice the ImageView used fill_parent for width and height.
The image cat is a small image and it will be zoomed in to fit the ImageView, and keep the width/height ratio at the same time.
My question is how to get the displayed size of the image? I tried:
imageView.getDrawable().getIntrinsicHeight()
But which it the original height of the image cat.
I tried:
imageView.getDrawable().getBounds()
But which returns Rect(0,0,0,0).
the following will work:
ih=imageView.getMeasuredHeight();//height of imageView
iw=imageView.getMeasuredWidth();//width of imageView
iH=imageView.getDrawable().getIntrinsicHeight();//original height of underlying image
iW=imageView.getDrawable().getIntrinsicWidth();//original width of underlying image
if (ih/iH<=iw/iW) iw=iW*ih/iH;//rescaled width of image within ImageView
else ih= iH*iw/iW;//rescaled height of image within ImageView
(iw x ih) now represents the actual rescaled (width x height) for the image within the view (in other words the displayed size of the image)
EDIT: I think a nicer way to write the above answer (and one that works with ints) :
final int actualHeight, actualWidth;
final int imageViewHeight = imageView.getHeight(), imageViewWidth = imageView.getWidth();
final int bitmapHeight = ..., bitmapWidth = ...;
if (imageViewHeight * bitmapWidth <= imageViewWidth * bitmapHeight) {
actualWidth = bitmapWidth * imageViewHeight / bitmapHeight;
actualHeight = imageViewHeight;
} else {
actualHeight = bitmapHeight * imageViewWidth / bitmapWidth;
actualWidth = imageViewWidth;
}
return new Point(actualWidth,actualHeight);
Here is a helper function to get the bounds of image in an imageView.
/**
* Helper method to get the bounds of image inside the imageView.
*
* #param imageView the imageView.
* #return bounding rectangle of the image.
*/
public static RectF getImageBounds(ImageView imageView) {
RectF bounds = new RectF();
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
imageView.getImageMatrix().mapRect(bounds, new RectF(drawable.getBounds()));
}
return bounds;
}
I guess a lot of people are coming from this example https://developer.android.com/training/animation/zoom.html and don't want to use android:scaleType="centerCrop" (maybe because the ImageView is in a constraint layout and you want to see the small picture uncroped) don't you worry, I got your back!
Just replace the entire block beginning with
// Adjust the start bounds to be the same aspect ratio as the final
// bounds using the "center crop" technique.
with the following
//adjust for scaled image to constraint
int realheight = ResourcesCompat.getDrawable(getResources(),imageResId,null).getIntrinsicHeight();
int realwidth = ResourcesCompat.getDrawable(getResources(),imageResId,null).getIntrinsicWidth();
// Adjust the start bounds to be the same aspect ratio as the final
// bounds using ueen's adjusteddimensions technique. This prevents undesirable
// stretching during the animation. Also calculate the start scaling
// factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
> (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
// after check whether height or width needs adjusting
if ((float) startBounds.width() / startBounds.height() < (float) realwidth / realheight) {
int adjustedheight = realheight*startBounds.width()/realwidth;
int adjustedoffset = (startBounds.height()-adjustedheight) / 2;
startScale = (float) adjustedheight / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
startBounds.offset(+0, +adjustedoffset);
} else {
int adjustedwidth = realwidth*startBounds.height()/realheight;
int adjustedoffset = (startBounds.width()-adjustedwidth) / 2;
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - adjustedwidth) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
startBounds.offset(+adjustedoffset, +0);
}
} else {
// Extend start bounds vertically
// after check whether height or width needs adjusting
if ((float) startBounds.width() / startBounds.height() > (float) realwidth / realheight) {
int adjustedwidth = realwidth*startBounds.height()/realheight;
int adjustedoffset = (startBounds.width()-adjustedwidth) / 2;
startScale = (float) adjustedwidth / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
startBounds.offset(+adjustedoffset, +0);
} else {
int adjustedheight = realheight*startBounds.width()/realwidth;
int adjustedoffset = (startBounds.height()-adjustedheight) / 2;
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - adjustedheight) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
startBounds.offset(+0, +adjustedoffset);
}
}
works like a charme,
you're welcome :)
Further explanation: as usual we check wheter the picture is taller than wide (expanded the height of the picture should match the height of expandedImageView) or vice versa. Then we check if the picture in the original (smaller) ImageView (thumbView) is matching the width or the heigth, so we can adjust for the space.
This way we achieve a smooth scaling animation while not croping the picture in the thumbView, no matter it's dimension (as they may change from device to device when using constarints) or that of the picture.
use
// For getting imageview height
imgObj.getMeasuredHeight()
// For getting imageview width
imgObj.getMeasuredWidth();
//For getting image height inside ImageView
imgObj.getDrawable().getIntrinsicHeight();
//For getting image width inside ImageView
imgObj.getDrawable().getIntrinsicWidth();