I have two questions:
I have an imageview that when I touch it I send it to an (X,Y) position. I have an S4 and a Galaxy Note 3 both with resolution (1920x1080) and density xxhdpi.
When i test them, i notice the imageView in different devices not send to same position. In Galaxy note the imageView is more to the right and above... why is this happening? have the same resolution and belongs to same density (xxhdpi)!
And how can i fix this for this example and all the screens?
Here is my XML code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/fondo_jugar"
tools:context="com.trucouruguayo.JugarActivity" >
<ImageView
android:id="#+id/imageViewCard1"
android:layout_width="101dp"
android:layout_height="142.50dp"
android:layout_alignLeft="#+id/imageViewMazo"
android:layout_alignTop="#+id/imageViewMazo"
android:layout_marginRight="-50dp"
android:contentDescription="#string/imageViewDescriptionCartd1"
android:src="#drawable/reverso"
android:scaleType="fitXY" />
<ImageView
android:id="#+id/imageViewCard2"
android:layout_width="101dp"
android:layout_height="142.50dp"
android:layout_alignLeft="#+id/imageViewMazo"
android:layout_alignTop="#+id/imageViewMazo"
android:layout_marginRight="-50dp"
android:contentDescription="#string/imageViewDescriptionCard2"
android:src="#drawable/reverso"
android:tag="imageViewCarta1"
android:scaleType="fitXY" />
...
</<RelativeLayout >
And this is the method that move the card:
Point point1 = new Point(-1460, 20);
/*Tira la carta al a mesa (sea de la maquina o del jugador)*/
private void tirarCartaALaMesa(View view, List<Carta> cartas, boolean tiraMaquina) {
cartas.remove(obtenerCartaFromView(view, cartas));
listaImageViewsTiradas.add(view);
indiceOrdenCartas ++;
animSetXY = new AnimatorSet();
ObjectAnimator scaleDownX = ObjectAnimator.ofFloat(view, "scaleX", 0.8f);
ObjectAnimator scaleDownY = ObjectAnimator.ofFloat(view, "scaleY", 0.8f);
ObjectAnimator rotation;
objectX = ObjectAnimator.ofFloat(view, "translationX", point1.x);
objectY = ObjectAnimator.ofFloat(view, "translationY", point1.y);
animSetXY.setInterpolator(new LinearInterpolator());
animSetXY.setDuration(500);
animSetXY.start();
}
Another question I realized is why point1 have to set (-1460, 20) to be in the top left corner and not for example (20,20). I think is because it tooks the (0,0) from the views position. Its a posibility to change that?
Greets
To solve this problem I calculated the X and Y co-ordinates programatically by using DisplayMetrics class like this:
DisplayMetrics metrics= new DisplayMetrics();
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
display.getMetrics(metrics);
int x=metrics.widthPixels/2; //2 can be changed according to the need
int y=metrics.heightPixels/2; //2 can be changed according to the need
And then You can make the image view appear at that coordinate.
Related
I am trying to figure out how to animate an image from left to right, and then from right to left.
For example imagine the following image:
At full resolution, the width of the image would exceed the width of the screen. So initially, only the left side of the image should be visible, but as the animation is running, slowly the right side becomes visible, eg.:
Once the right side of the image was fully uncovered, the animation starts in reverse:
Any ideas how to achieve this?
Android APIs or any techniques would be of great help.
Using ObjectAnimator.
Well, I didn't really know, how to size image in without cropping, so I made view's width 1000dp.
Here is solution:
ImageView imageView = findViewById(R.id.imageView);
imageView.post(() -> {
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
float width = imageView.getMeasuredWidth();
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0, -(width - point.x));
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setDuration(10000);
objectAnimator.start();
});
Let me explain.
First, I get size of device.
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize(point);
Then, I get width of view and creating ObjectAnimator
float width = imageView.getMeasuredWidth();
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0, -(width - point.x));
First argument is a view, that will be animated. Second argument tells objectAnimator what method (in this case setter) to use. We use translationX, so objectAnimator use setTranslationX().
All other arguments tells objectAnimator from where to where it should animate.
We substracting point.x from width for animation stops when edge of image is shown. Else, imageView will slide to the left until image disappears.
For example:
ObjectAnimator.ofFloat(imageView, "translationX", 0, 200, 100, 300);
says that image should go by axis X to the point 0, then 200, then 100 and in the end 300.
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
tells that after animation done, do it again, but in reverse.
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
tells that animation must repeat itself infinitely.
objectAnimator.setDuration(10000);
Duration of the animation in milliseconds.
Try with TranslateAnimation:
First, you have to know how big is the view:
ImageView picture = findViewById(R.id.picture);
int pictureWidth = picture.getWidth();
private void animation(View view, float pictureWidth) {
Animation offSet = new TranslateAnimation(0, - moveTo, 0, 0);
offSet.setInterpolator(new AccelerateDecelerateInterpolator()); // just to make a smooth movement
offSet.setDuration(durationInMillis);
}
The code above will move to the left showing the right side of the image,
you can add something similar to reverse the movement.
There are for sure better and more complete solutions, this is just (I think) the easier.
I need to show some Images inside the Frame (Multiple Image Frames), I have left and top coordinates correspondence to each Image.
For example, I have Image coordinates 100 from left and 100 from top, So what i need to do is just to show the image inside the ImageView which starts from (100,100).
Can anyone tell me how could i do that without Bitmap, because i need to show the list of Frames with Multiple Images inside RecyclerView.
I have used TouchImageView.class for Zoom and Move feature for creating the Frame.
1. Below screen shot while i am selecting layout, as you can clearly seen that both the images are quite close to each other. I mean that i am selecting half portion of each image.
2. Next is the Home screen at which i need to replot that image, I am having the left and top coordinate of both the images.But How can i plot that Image inside ImageView by some custom x and y.
Code snippet for populating image inside ImageView from left and top -
img.setPadding((int) x, (int) y, 0, 0);
But still not working.
Please suggest me something, i have done google search but didn't got the appropriate solution for my requirement.
Suggestions really appreciated.
Many Thanks in advance.
Kind Regards.
Update: I have added a sample app at the end of this post to illustrate this concept.
Second update: Added some code to to sample app to accommodate xy scaling
I have changed the sample app to include code that will fit the image, whether smaller or larger than the ImageView, to the view size while respecting the aspect ratio. This may not be exactly what you are looking for, but it should get you into the ballpark. This is sample code, so there are assuredly other things to accommodate in an actual application. For instance, xScale and yScale could go negative in extreme cases. I am not sure what happens if they do, but a crash is probably the result, so a bounds check is in order.
If I understand what you are trying to do, you want a fixed size ImageView to show different parts of a
larger image by setting custom (x,y) coordinates of the image to sit at the (0,0) coordinate of the ImageView.
This is basically a cropping of the image across the top and left edges.
There are probably many different ways to do this and which one works best for you will depend on your exact needs,
but here is one way:
Use the following, or some variation, in your code:
float xOffset = -100;
float yOffset = -100;
ImageView imageView = (ImageView) findViewById(R.id.shiftedView);
Matrix matrix = new Matrix();
matrix.setTranslate(xOffset, yOffset);
imageView.setImageMatrix(matrix);
Set up your ImageView something like the following. The key here is android:scaleType="matrix"
<ImageView
android:id="#+id/shiftedView"
android:layout_width="200dp"
android:layout_height="match_parent"
android:background="#FF00CCCC"
android:scaleType="matrix"
android:src="#drawable/image" />
I hope this helps you.
Sample App
Here is a quick mock-up of this concept. This app is composed of the main activity and one layout. You will have to supply an image that is the source for the ImageView to see how this works.
The seven buttons will shift the image left, right, up and down on a click within the layout of the ImageView. The "Fit X" and "Fit Y" buttons will scale the image to the corresponding dimension available within the ImageView. The "Reset" button will set the image back to the original default setting of the ImageView.
MainActivity.java
package com.example.imageshift;
import android.graphics.Matrix;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
ImageView imageView;
float xOffset = 0f;
float yOffset = 0f;
float xScale = 1.0f;
float yScale = 1.0f;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.shiftedView);
findViewById(R.id.shiftUp).setOnClickListener(this);
findViewById(R.id.shiftDown).setOnClickListener(this);
findViewById(R.id.shiftLeft).setOnClickListener(this);
findViewById(R.id.shiftRight).setOnClickListener(this);
findViewById(R.id.shiftReset).setOnClickListener(this);
findViewById(R.id.fitX).setOnClickListener(this);
findViewById(R.id.fitY).setOnClickListener(this);
}
public void doMatrixTransformations() {
Matrix matrix = new Matrix();
matrix.setTranslate(xOffset, yOffset);
matrix.postScale(xScale, yScale);
imageView.setImageMatrix(matrix);
}
#Override
public void onClick(View view) {
final int shiftAmount = 50;
switch (view.getId()) {
case R.id.shiftUp:
yOffset -= shiftAmount;
break;
case R.id.shiftDown:
yOffset += shiftAmount;
break;
case R.id.shiftLeft:
xOffset -= shiftAmount;
break;
case R.id.shiftRight:
xOffset += shiftAmount;
break;
case R.id.fitX:
xScale = (float) imageView.getWidth() / (
(float) imageView.getDrawable().getIntrinsicWidth() + xOffset);
yScale = xScale;
break;
case R.id.fitY:
yScale = (float) imageView.getHeight() /
((float) imageView.getDrawable().getIntrinsicHeight() + yOffset);
xScale = yScale;
break;
case R.id.shiftReset:
xOffset = 0;
yOffset = 0;
xScale = 1.0f;
yScale = 1.0f;
break;
}
doMatrixTransformations();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.imageshift.MainActivity">
<ImageView
android:id="#+id/shiftedView"
android:layout_width="200dp"
android:layout_height="match_parent"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:padding="2dp"
android:scaleType="matrix"
android:src="#drawable/image" />
<Button
android:id="#+id/shiftUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Up" />
<Button
android:id="#+id/shiftDown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftUp"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Down" />
<Button
android:id="#+id/shiftLeft"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftDown"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Left" />
<Button
android:id="#+id/shiftRight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftLeft"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Right" />
<Button
android:id="#+id/fitX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/shiftRight"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Fit X" />
<Button
android:id="#+id/fitY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/fitX"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Fit Y" />
<Button
android:id="#+id/shiftReset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/fitY"
android:layout_marginLeft="20dp"
android:layout_toRightOf="#id/shiftedView"
android:text="Reset" />
</RelativeLayout>
It sounds like you want to take two images and create a new image based on some combination of the two images.
There are lots of different ways to go about this. The most straightforward ways involve using Bitmaps. There's two main approaches I can see that are straightforward to accomplish this.
Position your image views how you like within some common parent and create a bitmap of that parent to use as your resulting image.
Create a bitmap yourself by drawing the images of your imageviews based on your own calculations that should mimic the layout structure you have.
Since you already seem to be showing the two images on the screen in the way you want the resulting image to look like, the first is going to be the most straightforward and won't require you to redo the calculations that your layouts and imageviews are already performing.
What we're going to do is take advantage of the fact that every view will draw itself onto a canvas and at the end of the day is just translating itself into an image itself. To get a bitmap of your parent layout you can simply create a bitmap of the same size and then tell your parent layout to draw itself onto a canvas of the bitmap, like so:
private Bitmap getCombinedBitmap(View parentView) {
Bitmap result = Bitmap.createBitmap(parentView.getWidth(), parentView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
parentView.draw(canvas);
return result;
}
If however you would like to not be bound by just whatever the parent layout is drawing to the screen, you can create the bitmap you want by drawing your images onto a similar canvas. Here I have implemented the first layout from your question:
private Bitmap getCombinedBitmap(Bitmap a, Bitmap b) {
int aWidth = a.getWidth() / 2;
int bWidth = b.getWidth() / 2;
int resultHeight = a.getHeight() < b.getHeight()? a.getHeight() : b.getHeight();
Bitmap result = new Bitmap(aWidth + bWidth, resultHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Rect aRect = new Rect(0, 0, aWidth, resultHeight);
Rect aResultRect = new Rect(0, 0, aWidth, resultHeight);
canvas.drawBitmap(a, aRect, aResultRect);
Rect bRect = new Rect(0, 0, bWidth, resultHeight);
Rect bResultRect = new Rect(bWidth, 0, bWidth, resultHeight);
canvas.drawBitmap(b, bRect, bResultRect);
return result;
}
In this example I opted for clearer code rather than more optimised code. You can optimise this by not creating as many Rects, etc and generally simplifying your calculations. But you can use this approach to recreate any of your layouts onto a single bitmap, you would just need to change the calculations.
What we are doing is first creating a bitmap that has a width of half the first image plus half the second image, and the height of the smallest of the two images. We then take a section of the first image that is the left most half width wise and as high as the result height wise and we draw that onto our result. We then do the same with the second image except this time the section is the right most half width wise.
Hope this helps.
I have to admit that I'm not 100% sure I understand what you want to do. If I understand correctly, what you need is to put an ImageView inside another element (I show an example with FrameLayout but you could use LinearLayout or RelativeLayout too). Then you set the padding on the outer element.
<!-- we put the ImageView inside a Frame so that we can change -->
<!-- the padding size to create borders -->
<FrameLayout
android:layout_width="140dp"
android:layout_height="140dp"
android:id="#+id/image_frame"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/image_view"
/>
</FrameLayout>
Then in the code: (use frame instead of img)
FrameLayout frame = (FrameLayout)itemLayout.findViewById(R.id.image_frame);
frame.setPadding((int) x, (int) y, 0, 0);
If I understand what you want to do, this will do it.
You can do it easily by merging BitMaps.
I have created a test project which loads 2 image bitmaps with Glide and then merges them in one BitMap using canvas and then set it on any ImageView.
You can also save the image in a file on SD Card to avoid heavy calculation each time. It looks like this (Gif - https://github.com/hiteshsahu/Android-Merge-Bitmaps-With-Glide/blob/master/Art/demo.gif ):-
I have used a single Image view as the background of my Frame layout and I have used seek bar to change the relative position of the movie poster Raees. You can change start X and Y coordinates of either of the bitmaps using those seekbars.
How I did it:-
This is the code snippet of how I did it. It is self-explanatory
Glide.with(context)
.load(R.drawable.raees)
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
// Add Bitmap in Array and load next bitmap
arts[0] = resource;
Glide.with(context)
.load(R.drawable.raees_edited)
.asBitmap()
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource1, GlideAnimation<? super Bitmap> glideAnimation) {
// Both Bitmap loaded do the Merging
arts[1] = resource1;
//Set seekbars Max
satrtX1SeekBar.setMax(arts[0].getWidth());
satrtX2SeekBar.setMax(arts[1].getWidth());
satrtY1SeekBar.setMax(arts[0].getHeight());
satrtY2SeekBar.setMax(arts[1].getHeight());
Bitmap bmOverlay = Bitmap.createBitmap(arts[0].getWidth(), arts[0].getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmOverlay);
if (shouldDrawLeftHalf) {
// Rect(left, top, right, bottom)
canvas.drawBitmap(arts[0],
new Rect(0, 0, arts[0].getWidth() / 2, arts[0].getHeight()),
new Rect(0, 0, arts[0].getWidth() / 2, arts[0].getHeight()), null);
} else {
canvas.drawBitmap(arts[0], startX1, startY1, null);
}
if (shouldDrawRightHalf) {
// Rect(left, top, right, bottom)
canvas.drawBitmap(arts[1],
new Rect(arts[1].getWidth() / 2, 0, arts[0].getWidth(), arts[1].getHeight()),
new Rect(arts[1].getWidth() / 2, 0, arts[1].getWidth(), arts[1].getHeight()), null);
} else {
canvas.drawBitmap(arts[1], startX2, startY2, null);
}
background.setImageBitmap(bmOverlay);
// saveToDisk("Test", bmOverlay);
}
});
}
});
You can download entire project from My GitHub
https://github.com/hiteshsahu/Android-Merge-Bitmaps-With-Glide
Eventually i have found the solution of my question -
Thanks for all the help provided.
So, basically i have view height, view width, top coordinate, left coordinate, Original Image Height and Original Image Width.
I need to calculate the aspect factor regarding View and Image.
Here is the formula for that -
float newAspectFactor = 0;
if (viewHeight > viewWidth) {
//Aspect factor for Height
newAspectFactor = viewHeight / bean.getPostMedia().get(i).getImg_height();
} else {
//Aspect factor for Width
newAspectFactor = viewWidth / bean.getPostMedia().get(i).getImg_width();
}
float imgNewHeight = newAspectFactor * imageHeight;
float imgNewWidth = newAspectFactor * imageWidth;
Logger.logsError(TAG, " Image New Height : " + imgNewHeight);
Logger.logsError(TAG, " Image New Width : " + imgNewWidth);
if (imgNewHeight < viewHeight) {
newAspectFactor = viewHeight / bean.getPostMedia().get(i).getImg_height();
} else if (imgNewWidth < viewWidth) {
newAspectFactor = viewWidth / bean.getPostMedia().get(i).getImg_width();
}
Top and Left coordinates.
float x = 42.0;
float y = 0.0;
Magic starts from here.
Matrix m = img.getImageMatrix();
RectF drawableRect = new RectF(x,
y,
((x * newAspectFactor) + viewWidth) / newAspectFactor,
imageHeight);
RectF viewRect = new RectF(0,
0,
viewWidth,
viewHeight);
m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.FILL);
img.setImageMatrix(m);
img.setScaleType(ImageView.ScaleType.MATRIX);
imageLoaderNew.displayImage("IMAGE URL", img, optionsPostImg);
I have 4 image view I want to move image 1 to position of image 2 with move and scale animation.
[
What I have tried for scaling
ObjectAnimator scaleX = ObjectAnimator.ofFloat(img1, "scaleX", (float) img2.getWidth());
ObjectAnimator scaleY = ObjectAnimator.ofFloat(img1, "scaleY", (float) img2.getHeight());
scaleX.setDuration(2000);
scaleY.setDuration(2000);
AnimatorSet scale = new AnimatorSet();
scale.play(scaleX).with(scaleY);
scale.start();
You can save a lot of code with ViewPropertyAnimator plus you can combine multiple animations in one line :
img1.animate()
.scaleX(toX)
.scaleY(toY)
.translationX(toX)
.translationY(toY)
.setDuration(2000);
There are also methods :
.scaleXBy(byX)
.translationYBy(byY)
that scale/translate the methods BY the given value
For the translation you have to keep in mind that :
img2.getX();
img2.getY();
only gives you the coordinates of the upper left corner of the view img2 .
You probably want to animate to the center of img2 which would be
img2.getX() / 2;
img2.getY() / 2;
All combined something like this should work :
img1.animate()
.scaleX(...)
.scaleY(...)
.translationX(img2.getX() / 2)
.translationY(img2.getY() / 2)
.setDuration(2000);
EDIT : I found this page on the developer training site you can check out :
Zooming a view
I want to mimic the animation on 4 Pics 1 Word, wherein the user taps a letter and flies somewhere in the screen. This letter can be a Button or an ImageView. Please refer to the image below:
Is this only possible with Corona SDK / Lua? How will I do it in Java? I'm just a beginner so I don't know how to start this so please don't ask any code that I already tried.
You can use ObjectAnimator class for performing animations on Android views. For example, if you have to animate the y property of the view mView you can write:
float finalValue = 10f;
ObjectAnimator yAnimator = ObjectAnimator.ofFloat(mView, View.Y, finalValue);
yAnimator.start();
For animating more than one property you can write:
float finalX = 20f;
float finalY = 30f;
ObjectAnimator xAnimator = ObjectAnimator.ofFloat(mView, View.X, finalX);
ObjectAnimator yAnimator = ObjectAnimator.ofFloat(mView, View.Y, finalY);
new AnimatorSet().playTogether(xAnimator, yAnimator);
On a layout I want to scale the background image (keeping its aspect ratio) to the space allocated when the page gets created. Does anyone have an idea how to do this?
I am using layout.setBackgroundDrawable() and a BitmapDrawable() to set gravity for clipping and filling, but don't see any option for scaling.
To customize background image scaling create a resource like this:
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center"
android:src="#drawable/list_bkgnd" />
Then it will be centered in the view if used as background. There are also other flags: http://developer.android.com/guide/topics/resources/drawable-resource.html
Haven't tried to do exactly what you want, but you can scale an ImageView using android:scaleType="fitXY"
and it will be sized to fit into whatever size you give the ImageView.
So you could create a FrameLayout for your layout, put the ImageView inside it, and then whatever other content you need in the FrameLayout as well w/ a transparent background.
<FrameLayout
android:layout_width="fill_parent" android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="#drawable/back" android:scaleType="fitXY" />
<LinearLayout>your views</LinearLayout>
</FrameLayout>
There is an easy way to do this from the drawable:
your_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#color/bg_color"/>
<item>
<bitmap
android:gravity="center|bottom|clip_vertical"
android:src="#drawable/your_image" />
</item>
</layer-list>
The only downside is that if there is not enough space, your image won't be fully shown, but it will be clipped, I couldn't find an way to do this directly from a drawable. But from the tests I did it works pretty well, and it doesn't clip that much of the image. You could play more with the gravity options.
Another way will be to just create an layout, where you will use an ImageView and set the scaleType to fitCenter.
Use image as background sized to layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ImageView
android:id="#+id/imgPlaylistItemBg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:maxHeight="0dp"
android:scaleType="fitXY"
android:src="#drawable/img_dsh" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</FrameLayout>
To keep the aspect ratio you have to use android:scaleType=fitCenter or fitStart etc. Using fitXY will not keep the original aspect ratio of the image!
Note this works only for images with a src attribute, not for the background image.
When you set the Drawable of an ImageView by using the setBackgroundDrawable method, the image will always be scaled. Parameters as adjustViewBounds or different ScaleTypes will just be ignored. The only solution to keep the aspect ratio I found, is to resize the ImageView after loading your drawable. Here is the code snippet I used:
// bmp is your Bitmap object
int imgHeight = bmp.getHeight();
int imgWidth = bmp.getWidth();
int containerHeight = imageView.getHeight();
int containerWidth = imageView.getWidth();
boolean ch2cw = containerHeight > containerWidth;
float h2w = (float) imgHeight / (float) imgWidth;
float newContainerHeight, newContainerWidth;
if (h2w > 1) {
// height is greater than width
if (ch2cw) {
newContainerWidth = (float) containerWidth;
newContainerHeight = newContainerWidth * h2w;
} else {
newContainerHeight = (float) containerHeight;
newContainerWidth = newContainerHeight / h2w;
}
} else {
// width is greater than height
if (ch2cw) {
newContainerWidth = (float) containerWidth;
newContainerHeight = newContainerWidth / h2w;
} else {
newContainerWidth = (float) containerHeight;
newContainerHeight = newContainerWidth * h2w;
}
}
Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
imageView.setBackgroundDrawable(new BitmapDrawable(copy));
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(params);
imageView.setMaxHeight((int) newContainerHeight);
imageView.setMaxWidth((int) newContainerWidth);
In the code snippet above is bmp the Bitmap object that is to be shown and imageView is the ImageView object
An important thing to note is the change of the layout parameters. This is necessary because setMaxHeight and setMaxWidth will only make a difference if the width and height are defined to wrap the content, not to fill the parent. Fill parent on the other hand is the desired setting at the beginning, because otherwise containerWidth and containerHeight will both have values equal to 0.
So, in your layout file you will have something like this for your ImageView:
...
<ImageView android:id="#+id/my_image_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
...
This is not the most performant solution, but as somebody suggested instead of background you can create FrameLayout or RelativeLayout and use ImageView as pseudo background - other elements will be position simply above it:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ImageView
android:id="#+id/ivBackground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:scaleType="fitStart"
android:src="#drawable/menu_icon_exit" />
<Button
android:id="#+id/bSomeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="61dp"
android:layout_marginTop="122dp"
android:text="Button" />
</RelativeLayout>
The problem with ImageView is that only scaleTypes available are:
CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER,FIT_END, FIT_START, FIT_XY, MATRIX
(http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html)
and to "scale the background image (keeping its aspect ratio)" in some cases, when you want an image to fill the whole screen (for example background image) and aspect ratio of the screen is different than image's, the necessary scaleType is kind of TOP_CROP, because:
CENTER_CROP centers the scaled image instead of aligning the top edge to the top edge of the image view and FIT_START fits the screen height and not fill the width. And as user Anke noticed FIT_XY doesn't keep aspect ratio.
Gladly somebody has extended ImageView to support TOP_CROP
public class ImageViewScaleTypeTopCrop extends ImageView {
public ImageViewScaleTypeTopCrop(Context context) {
super(context);
setup();
}
public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
super(context, attrs);
setup();
}
public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup();
}
private void setup() {
setScaleType(ScaleType.MATRIX);
}
#Override
protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {
float frameWidth = frameRight - frameLeft;
float frameHeight = frameBottom - frameTop;
if (getDrawable() != null) {
Matrix matrix = getImageMatrix();
float scaleFactor, scaleFactorWidth, scaleFactorHeight;
scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();
if (scaleFactorHeight > scaleFactorWidth) {
scaleFactor = scaleFactorHeight;
} else {
scaleFactor = scaleFactorWidth;
}
matrix.setScale(scaleFactor, scaleFactor, 0, 0);
setImageMatrix(matrix);
}
return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
}
}
https://stackoverflow.com/a/14815588/2075875
Now IMHO would be perfect if somebody wrote custom Drawable which scales image like that. Then it could be used as background parameter.
Reflog suggests to prescale drawable before using it. Here is instruction how to do it:
Java (Android): How to scale a drawable without Bitmap?
Although it has disadvantage, that upscaled drawable/bitmap will use more RAM, while scaling on the fly used by ImageView doesn't require more memory. Advantage could be less processor load.
The Below code make the bitmap perfectly with same size of the imageview. Get the bitmap image height and width and then calculate the new height and width with the help of imageview's parameters. That give you required image with best aspect ratio.
int bwidth=bitMap1.getWidth();
int bheight=bitMap1.getHeight();
int swidth=imageView_location.getWidth();
int sheight=imageView_location.getHeight();
new_width=swidth;
new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
imageView_location.setImageBitmap(newbitMap)
What Dweebo proposed works. But in my humble opinion it is unnecessary.
A background drawable scales well by itself. The view should have fixed width and height, like in the following example:
< RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/black">
<LinearLayout
android:layout_width="500dip"
android:layout_height="450dip"
android:layout_centerInParent="true"
android:background="#drawable/my_drawable"
android:orientation="vertical"
android:padding="30dip"
>
...
</LinearLayout>
< / RelativeLayout>
One option to try is to put the image in the drawable-nodpi folder and set background of a layout to the drawable resource id.
This definitely works with scaling down, I haven't tested with scaling up though.
Kotlin:
If you needed to draw a bitmap in a View, scaled to FIT.
You can do the proper calculations to set bm the height equal to the container and adjust width, in the case bm width to height ratio is less than container width to height ratio, or the inverse in the opposite scenario.
Images:
// binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
// bm is the Bitmap
val ch = binding.fragPhotoEditDrawCont.height
val cw = binding.fragPhotoEditDrawCont.width
val bh = bm.height
val bw = bm.width
val rc = cw.toFloat() / ch.toFloat()
val rb = bw.toFloat() / bh.toFloat()
if (rb < rc) {
// Bitmap Width to Height ratio is less than Container ratio
// Means, bitmap should pin top and bottom, and have some space on sides.
// _____ ___
// container = |_____| bm = |___|
val bmHeight = ch - 4 //4 for container border
val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
}
else {
val bmWidth = cw - 4 //4 for container border
val bmHeight = 1f/rb * cw
binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
}
you'll have to pre-scale that drawable before you use it as a background
You can use one of following:
android:gravity="fill_horizontal|clip_vertical"
Or
android:gravity="fill_vertical|clip_horizontal"