Android: how to prevent dynamic background drawable from stretching - android

I need to set the layout's background dynamically by calling myView.setBackgroundDrawable(BitmapDrawable bdb), but I want to crop the image rather than stretching it in the layout, is there a way doing it?
I was trying to create the drawable as below to set the gravity, but the challenge is I can't hardcode the android:src to a static drawable since the image source has to be dynamic.
<bitmap android:src="#drawable/background" android:gravity="center" />
Any suggestion would be really appreciated!

I believe the android:gravity attributes you are looking for are clip_vertical and clip_horizontal, but a word of advice. These attributes may not behave the way you think they should. You cannot use them to clip an image in both directions, only one. The following code:
<bitmap
android:src="#drawable/background"
android:gravity="clip_vertical|clip_horizontal" />
does not allow the image to be cropped in both directions...it actually fits the image inside the view as if you had not set either one. It also can be confusing that setting a clip parameter essentially sets that direction to fit the view, with the other direction being cropped out. A few examples:
<!-- Force fit top/bottom, crop left/right with image centered -->
<bitmap
android:src="#drawable/background"
android:gravity="clip_vertical" />
<!-- Force fit top/bottom, align image left and crop right edge -->
<bitmap
android:src="#drawable/background"
android:gravity="left|clip_vertical" />
<!-- Force fit left/right, crop top/bottom with image centered -->
<bitmap
android:src="#drawable/background"
android:gravity="clip_horizontal" />
<!-- Force fit left/right, align top and crop bottom egde -->
<bitmap
android:src="#drawable/background"
android:gravity="top|clip_horizontal" />
Notice that these settings allow an image larger than the view to be cropped, but if the image is smaller than the view, its contents will still be stretched to fit. In order to govern how an image will behave in a <bitmap> tag when it is smaller than the view, look at tileMode. Note that tileMode and gravity cannot be used together; gravity will be ignored if both are included.
If you need more dynamic control of how the image scales in addition to this, and you do not want to use an ImageView, you could also wrap the resulting Drawable in a ScaleDrawable and configure the x/y scale percentages in Java code based on the measured view sizes.

You want to use ImageView.ScaleType.CENTER_CROP

Related

Stretching a one-pixel wide gradient image in a drawable, or otherwise

I have a PNG file which is a one-pixel-wide, 283-pixel-tall gradient image, which I need to stretch across the background of an ImageView, stretching only horizontally. I attempted to set the asset as a background to an ImageView like this:
<ImageView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/gradient_tile"
android:layout_gravity="bottom|center"
android:scaleType="matrix"/>
but that just creates a one-pixel line in the middle of the parent view.
Is there a way to do this, or do I need to request a wider image, and use a 9-patch?
Thanks in advance.
UPDATE:
I ended up having to set minimum height properties in the XML as follows:
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="119dp"
android:background="#drawable/gradient_tile_drawable"
android:id="#+id/tiledGradientBackground"
android:layout_gravity="bottom|center"
android:scaleType="matrix"/>
...and then set minimumWidth to the width of the parent view in code. Not sure why this solved it, but it did...
int width = holder.container.getResolvedWidth();
holder.tiledGradientBackground.setMinimumWidth(width);
Try this (tiling instead of stretching):
Put in your drawable folder a file called bg.xml:
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/your_1px_wide_image"
android:tileMode="repeat"
/>
and set it as your layout background
android:background="#drawable/bg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
I tested it and it works fine.
This image
is giving this result.
Note that I put some extra padding - this screen is 320*480 wide, so the gradient is approx 1/3rd the total screen height (including title and status bars + the extra padding)
scaletype effects the src image of ImageView and not the background, if the image view is used only for background set the image as src and used fitXY scaletype.
You should create a drawable like Klaus66 & CommonsWare suggested and set it as a background.
Actually if you have a 1px gradient you probably can just create a GradientDrawable xml, will look better across different devices.
http://developer.android.com/reference/android/graphics/drawable/GradientDrawable.html
You shouldn't use an extra ImageView just set it as the background of your top layout or even the background of your theme, see my answer here: Android SplashScreen

Center an image in a container scale it and crop the remaining

I have this scenario. So the image is larger than the container
I want it to scale down or scale up depending on the container size but at the same time put it on the center and crop the remaining (horizontally).
So I did something like this.
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/header_long"
android:gravity="center"
android:antialias="true"
android:dither="true"/>
However it looks like this now. The top, bottom, left and right is cropped.
I want it to be something like this.
try this
android:scaleType="centerCrop"
Like ben75 proposes, you can put your bitmap into am ImageView and use
android:scaleType="centerCrop"
If you want to use your image as a background, consider making it a 9-patch image. They define areas that may be stretched if the image is too small. They also define padding. Note that 9-patch images grow as needed but they never shrink, so make them as small as the smallest size you like them to get.

Android Overlaying images in layer-list

I am trying to create a custom drawable which includes a 9-patch background image (named custom_bg below) and a regular png image (accordion_up). The latter should not be scaled.
I tried several variations of something along the lines of:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/custom_bg"/>
<item
android:layout_width="10dp"
android:layout_height="10dp"
android:drawable="#drawable/accordion_up"
android:gravity="right" />
</layer-list>
Tried different values for layout_width/layout_height and also width/height but all I get is the accordion image is stretched to fill the entire view. I would like it to float at the top right of the view instead, scaled only to match the dpi of the device, but not expanded to fit the view. How can I do that?

Replacing existing drwable in imageview is wrong scaled on some devices

The Situation:
I have a imageview with a layer list of two drwables.
The frame drwable is 800x400px and the cover drawable is 800x380px.
Both images reside in the folder drawable
<ImageView
android:id="#+id/preview_img_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="0.9"
android:adjustViewBounds="true"
android:src="#drawable/result_preview"/>
The Drwable layer list in drawable/result_preview
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/cover">
<bitmap
android:gravity="left|top"
android:src="#drawable/cover" />
</item>
<item
android:id="#+id/frame">
<bitmap
android:gravity="left|top"
android:src="#drawable/frame" />
</item>
</layer-list>
This setup works as expected the cover is displayed framed on all devices. Now the user can replace the sample cover with another cover of the same size.
Replacing the cover in the layer list and leave the frame as is.
LayerDrawable layer = (LayerDrawable) getResources().getDrawable(R.drawable.result_preview);
InputStream coverIs = getContentResolver().openInputStream(Uri.parse(coverUri));
this.drwCover = (BitmapDrawable) BitmapDrawable.createFromStream(coverIs, coverUri);
drwCover.setGravity(Gravity.LEFT | Gravity.TOP);
//drwCover.getBitmap().setDensity(160);
layer.setDrawableByLayerId(R.id.cover, drwCover);
imageView.setImageDrawable(layer);
The Problem:
Replacing the current cover with a new one (same dimensions as old) produces different results depending on the Device used.
On a device with a 3.2 screen and a 480x320 resolution the cover replaced fits in the frame. On a device with a 3.7 and a 800x480 resolution the replaced cover is displayed smaller then the old one.
What I found out is that the drwable in the imageview on the small device has a intrinsic height of 800x400 same as the dimensions of the drawable.
On the bigger screen the intrinsic height of the drawable is 30% bigger.
I know the the intrinsic values may differ from screen to screen.
What I was expecting is that the drawable that replaces the old one should will be scaled up the same way the old one was to +30%. But this did not happen.
Question:
Is there a option to tell the imageview or the layer list to Adpt itself? I think there should be a way to do so because the system did it already at the beginning.
First, I'd suggest doing the cover/frame differently:
make your frame a nine-patch drawable, so you can define in the nine-patch the padding that will remain visible when the cover is drawn on top of it.
put the frame drawable as a background
set the cover as the src of the image, and not the layer list
don't forget to set a scaleType for your ImageView, play with the different options, so suit your needs.
<ImageView
android:id="#+id/preview_img_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="0.9"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:background="#drawable/frame"
android:src="#drawable/cover"/>
For other having the same problem. 9patch png is a solution to a problem I don't have.
So far the only thing that came close to solve the problem was to put my resources into the folder drawable-nodpi. Here is the reference to the docs

Expand clickable area of an ImageView by using padding?

I have an ImageView, and I want it to be clickable. The image itself is only 32x32 pixels, but I want the clickable region to be larger so it's easier to hit. I was hoping I could set the layout margin values to increase the size of the imageview's clickable area:
<ImageView
layout_width="32dip"
layout_height="32dip"
layout_margin="20dip" />
That doesn't work, what I could do is just recreate the image resource to have a larger transparent region around it. That's a bit annoying because it's hard to tweak if I ever need to change the clickable region. It's not just a single png either, it's a statelistdrawable so I have to resize 3 different pngs if I ever need to tweak the clickable area.
Anything else I can do?
Thanks
Use padding. layout margins are used if for inserting space outside the boundary of the view.
for equal padding on all sides
<ImageView
android:padding="20dip" />
or to set the padding on each side
<ImageView
android:paddingLeft="10dip"
android:paddingRight="15dip"
android:paddingTop="23dip"
android:paddingBottom="12dip" />
Hope that helps !
Instead of resizing the image (Peter Knego's answer) or increasing padding (Saimon's answer) I suggest to set the minWidth and minHeight attributes in combination with scaleType="center":
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="40dp"
android:minHeight="40dp"
android:scaleType="center"
android:src="#drawable/your_image" />
That makes sure that small images have at least a size of 40x40dp while the image is not up-scaled if it's smaller. If one day the image is going to be replaced with another image larger than minWidth and minHeight it will not grow larger than 40x40dp as it gets downscaled. Thus a minimum clickable dimension of 40x40dp is always guaranted and the image is always displayed nicely.
Suggestions (never tried it myself):
Create ImageView as large as you want than put image into it without scaling
ImageView.setScaleType(ImageView.ScaleType.CENTER).
Brute force approach: create new larger png that has original image centered and the rest of it is transparent.
Use like below; where width/height is your touch radius
<ImageView
android:id="#+id/editProfile"
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="8dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="#drawable/ic_edit"
android:scaleType="fitCenter"
/>
In the above code, I wanted my src size to be viewed as 24dp width/height. And touch radius of 32dp. so I had used padding of 8dp.

Categories

Resources