Scale and crop image in XML - android

I'm trying to scale and crop image at the same time and show it from left to right screen edge. I receive image that is just little bit wider than users screen and I'm able to scale it like this (XML):
<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:focusable="false"
/>
But this is what I get:
I would like to align image to top right like so:
Is this possible? I've tried all scaleTypes but noting works, image is either scaled to fit by X and Y (fitXY, fitStart) or image cropped but centered (centerCrop). I need something like android:scaleType="cropStart"

<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:paddingLeft="half of your screen width"
android:paddingBottom="half of your screen height"
android:adjustViewBounds="true"
android:focusable="false"
/>
You can set padding to move image left or right and also top and bottom padding to move up and down

As I didn't find a way to deal with this situation through xml (views) I turned (as #serenskye suggested) to code. Here's my code, I hope it helps (ps: I've changed my logic a little bit, I wanted to fit image by width so I've scaled it to predefined imageWidght and then cropped it to imageHeight)
//bm is received image (type = Bitmap)
Bitmap scaledImage = null;
float scaleFactor = (float) bm.getWidth() / (float) imageWidth;
//if scale factor is 1 then there is no need to scale (it will stay the same)
if (scaleFactor != 1) {
//calculate new height (with ration preserved) and scale image
int scaleHeight = (int) (bm.getHeight() / scaleFactor);
scaledImage = Bitmap.createScaledBitmap(bm, imageWidth, scaleHeight, false);
}
else {
scaledImage = bm;
}
Bitmap cropedImage = null;
//if cropped height is bigger then image height then there is no need to crop
if (scaledImage.getHeight() > imageHeight)
cropedImage = Bitmap.createBitmap(scaledImage, 0, 0, imageWidth, imageHeight);
else
cropedImage = scaledImage;
iv.setImageBitmap(cropedImage);

add
android:layout_gravity="center"

Related

drawable fit <bitmap> width to the view while keeping aspect ratio

Let's say I have a horizontally long image, "grass". Now, I want to use it as a background image for views, but I want to dock it to the bottom. After searching the web, I discovered that I need to wrap the image as a drawable like:
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/grass"
android:gravity="bottom|left" />
The problem is that the image is clipped to the right. So I have tried bottom|left|right but then the aspect ratio was not kept. Can I make the image fit horizontally, but keep the aspect ratio by automatically scaling it vertically? Or is this not possible with drawables?
what your searching for is centerInside ScaleType . please remove the ScaleType from the ImageView and try this :
public static synchronized Bitmap centerInside(Bitmap bitmap,int width,int height){
if(bitmap.getWidth() == bitmap.getHeight()){
Log.i("crop","already matched");
return Bitmap.createScaledBitmap(bitmap, width, height, true);
}
//int size = width > height ? width : height;
float scale = ImageUtils.calculateImageSampleSize(bitmap.getWidth(), bitmap.getHeight(),width,height);
width = (int) ((float)bitmap.getWidth() / scale);
height = (int) ((float)bitmap.getHeight() / scale);
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
if (bitmap.getWidth() >= bitmap.getHeight()){
bitmap = Bitmap.createBitmap(
bitmap,
bitmap.getWidth()/2 - bitmap.getHeight()/2,
0,
bitmap.getHeight(),
bitmap.getHeight()
);
}else{
bitmap = Bitmap.createBitmap(
bitmap,
0,
bitmap.getHeight()/2 - bitmap.getWidth()/2,
bitmap.getWidth(),
bitmap.getWidth()
);
}
return bitmap;
}
You cannot achieve this with XML only. If you don't want to have to draw the bitmap programmatically, you wrap an ImageView in a RelativeLayout and set the ImageView's scaleType property:
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/grass"
android:scaleType="fitXY" />
</RelativeLayout>
If I understand Your requirement right Your image need to stretch vertically and be in bounds horizontally.
A good solution I could suggest is creating a 9 patch image out of Your drawable and define the area where the image can stretch.
You can use this tutorial for reference.

Android: how to set the height/width of the src image of a ImageButton?

Android: how to set the height/width of the image(src image intead of the background) of a ImageButton?
I want to set the height/width of the top level image(not the background) image, the size of image should be customized instead of fullfill the button automatically
You can just Use the scaleType attribute of ImageButton.
Set it to fitXY or fitCenter or anything else according to your need.
like this
<ImageButton
android:id="#+id/imageButton"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitCenter"
android:src="#drawable/some_image">
</ImageButton>
You can use Bitmap.createScaledBitmap to scale the image to the size you want.
Bitmap image = ... //load image from your source
image = Bitmap.createScaledBitmap(image, desiredHeight, desiredWidth, true);
xml
<ImageButton
android:id="#+id/picture_id"
android:layout_width="10dp" //here width with unit. 5 dp exemple
android:layout_height="3dp" //here height with unit. 3 dp exemple
android:src="#drawable/picture_name"
/>
EDIT: (java code)
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.android);
int width = bitmapOrg.width();
int height = bitmapOrg.height();
int newWidth = 200;
int newHeight = 200;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
matrix.postRotate(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
ImageView imageView = new ImageView(this);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
// center the Image
imageView.setScaleType(ScaleType.CENTER);
// add ImageView to the Layout
linLayout.addView(imageView,
new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
)
);
I had same problem... change the size of 'src' image of image button(not the size of button just image only).
what i did--
I moved those '.png' file from 'drawable' folder to 'drawable-hdpi' folder.
weird... but it worked.
thanks,
set padding and scaletype to centerInside
Padding will help you customize the source image to provide the required height and width.
<ImageButton
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/location"
android:padding="10dp"
android:scaleType="centerInside"
android:background="#drawable/blue_circle_border"/>

Increase image beyond the screen size

I want to display an image on the screen but the image edges will be larger than the size of the screen itself.
IE if the maximum screen width is X then I want the width of the image will be X +100 and maximum screen height is Y then the image height is Y +100.
I want this option for the ability to move the image to the right / left and still the image will be shown on the whole screen.
For this purpose I use RelativeLayout with requestLayout function:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<ImageView
android:id="#+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
</RelativeLayout>
And in the code generate an appropriate image:
Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
// example code with bigger dimensions than the screen
Bitmap b = Bitmap.createBitmap(width + 100, height + 100, Bitmap.Config.ARGB_8888);
// draw smth on the bitmap
Canvas c = new Canvas(b);
Paint p = new Paint();
p.setColor(Color.RED);
c.drawCircle(b.getWidth() / 2,
b.getHeight() / 2,
Math.min(b.getWidth(), b.getHeight()) / 2,
p);
// set the image
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap(b);
// call this method to change imageview size
iv.requestLayout();

Resizing or Clipping after a rotation

I have a 10x10 tablelayout with each cell containing an ImageView. I simulate the moving across this gamegrid by updating the pictures in each grid on a move event. I also rotate the image to align the image with the direction of travel. My problem is that as the image rotates it causes my cell rows and columns to expand to fit the rotated image.
What I would like is to have the image cropped to remain within the dimensions of the table row and columns. If that isnt possible then I would like to scale the image to remain within the dimensions of the original table cell.
Another potential issue is that the images are getting scaled initially to fit into the cells (this is done automatically as the table expands to fill the screen). I don't want to change that behavior. I just want to trim off the corners of the image as they pass the original boundaries of the table cell when they are rotated.
My rotation code is below:
Matrix matrix = new Matrix();
matrix.postRotate((float) (360 - sector.getShip().getHeading()));
Bitmap bMap = BitmapFactory.decodeResource(image.getContext().getResources(), getShip().getGridSymbol());
image.setImageBitmap(Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), matrix, true));
image.setImageResource(sector.getShip().gridParams.default);
My table configuration is below:
<TableLayout android:id="#+id/quadrantGrid" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignWithParentIfMissing="true" android:stretchColumns="*" android:background="#000000">
...
<TableRow>
<TextView android:text="1" android:background="#404040" android:gravity="center"></TextView>
<ImageView android:id="#+id/grid00"></ImageView>
<ImageView android:id="#+id/grid01"></ImageView>
<ImageView android:id="#+id/grid02"></ImageView>
<ImageView android:id="#+id/grid03"></ImageView>
<ImageView android:id="#+id/grid04"></ImageView>
<ImageView android:id="#+id/grid05"></ImageView>
<ImageView android:id="#+id/grid06"></ImageView>
<ImageView android:id="#+id/grid07"></ImageView>
<ImageView android:id="#+id/grid08"></ImageView>
<ImageView android:id="#+id/grid09"></ImageView>
</TableRow>
...
UPDATE:
I found a solution, though I'm not sure its the most efficient solution.
All my images are 25x25. And so the OS figures how to scale those images to fit into the cell. The solution just performs two operations. First it rotates, and then it creates a smaller image from the rotated image. (note: When I created my image I made sure the important parts of the image would be contained within a circle with a diameter of the width of the image to allow for this type of cropping).
My code is below. I would prefer to perform both operations within one method, but Im not sure how so I just break it down into two steps.
Matrix matrix = new Matrix();
matrix.postRotate((float) (360 - sector.getShip().getHeading()));
Bitmap bMap = BitmapFactory.decodeResource(image.getContext().getResources(), getShip().getGridSymbol());
bMap = Bitmap.createBitmap(bMap, 0, 0, 25, 25, matrix, true);
int x = (bMap.getWidth() - 25) / 2;
int y = (bMap.getHeight() - 25) / 2;
image.setImageBitmap(Bitmap.createBitmap(bMap, x, y, 25, 25));
Try adding
android:scaleType="centerCrop"
to each ImageView item in your xml layout.
The final solution has to leverage the "getMeasuredHeight()" of the image. I didnt have to use the getMeasuredWidth() because for my particular layout the height is the limiting factor.
Here is the final bitmap code that works:
private Bitmap getRotatedShipBitmap(SectorGridImageView image,
Bitmap bitmap, Matrix matrix) {
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
int yHeight = (image.getMeasuredHeight() == 0) ? bitmap.getHeight()
: image.getMeasuredHeight();
int xWidth = yHeight;
int x = (bitmap.getWidth() - xWidth) / 2;
int y = (bitmap.getHeight() - yHeight) / 2;
return Bitmap.createBitmap(bitmap, x, y, xWidth, yHeight);
}

Android: high quality image resizing / scaling

I need to scale down images coming from a Network stream without losing quality.
I am aware of this solution Strange out of memory issue while loading an image to a Bitmap object but it is too coarse - inSampleSize is an integer and does not allow finer control over the resulting dimensions. That is, I need to scale images to specific h/w dimensions (and keeping aspect ratio).
I dont mind having a DIY bicubic/lancoz algorithm in my code but I cant find any examples that would work on Android as they all rely on Java2D (JavaSE).
EDIT:
Ive attached a quick source. The original is 720x402 HD screen capture. Please ignore the top 2 thumbnails. The top large image is resized automatically by android (as part of layout) to about 130x72. It is nice and crisp. The bottom image is resized with API and has severe artifacting
I've also tried using the BitmapFactory and, as I said earlier, it has two problems - no way to scale to exact size and the scaled image is blurry.
Any ideas on how to fix the artifcating?
Thanks, S.O.!
package qp.test;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;
public class imgview extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
Bitmap resized = getResizedBitmap(original, 130);
//Bitmap resized = getResizedBitmap2(original, 0.3f);
System.err.println(resized.getWidth() + "x" + resized.getHeight());
ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
image.setImageBitmap(resized);
}
private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float aspect = (float)width / height;
float scaleWidth = newWidth;
float scaleHeight = scaleWidth / aspect; // yeah!
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth / width, scaleHeight / height);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
bm.recycle();
return resizedBitmap;
}
private Bitmap getResizedBitmap2(Bitmap bm, float scale) {
/* float aspect = bm.getWidth() / bm.getHeight();
int scaleWidth = (int) (bm.getWidth() * scale);
int scaleHeight = (int) (bm.getHeight() * scale);
*/
// original image is 720x402 and SampleSize=4 produces 180x102, which is
// still too large
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 4;
return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
}
}
And the layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<!-- <TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="hullo" android:background="#00ff00"
/>
-->
<ImageView android:id="#+id/ImageViewThumbAuto"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402" />
<ImageView android:id="#+id/ImageViewThumbManual"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402"
android:layout_toRightOf="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullAuto" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullManual" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewFullAuto"
/>
</RelativeLayout>
You can use BitmapFactory.Options with BitmapFactory.decode function(s),
using inDensity and inTargetDensity
Example: you have 1600x1200 size of image and want to resize to 640x480
then 'inDensity'=5 and 'inTargetDensity'=2 (1600x2 equal to 640x5).
Hoping this help.
The top large image is simply scaled down to 450px by the layout so no artifacts.
The artifacts of the bottom large image result from scaling it down to 130px wide and then up again to about 450px by the layout. So the artifacts are made by your scaling. Try
Bitmap resized = getResizedBitmap(original, 450);
in your code and it should be fine. However, you need to adapt that to the actuall screen width of the phone either.
Briefly, good downscaling algorithm (not nearest neighbor like) consists of 2 steps:
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

Categories

Resources