How to have a Fullscreen Background Image (with center-crop) that doesn't Resize
Solution 1
try this LayerDrawable (res/drawable/backlayer.xml):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
>
<item>
<shape>
<solid android:color="#f00" />
</shape>
</item>
<item>
<bitmap
android:gravity="top" android:src="@drawable/back1">
</bitmap>
</item>
</layer-list>
and set it to your top level layout: android:background="@drawable/backlayer"
UPDATE: try this BitmapDrawadle, set it to top level layout (setBackgroundDrawable()), if simpleMapping == true is good enough you can remove "else" branch:
class D extends BitmapDrawable {
private Matrix mMatrix = new Matrix();
private int moldHeight;
public D(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.height() > moldHeight) {
moldHeight = bounds.height();
Bitmap b = getBitmap();
RectF src = new RectF(0, 0, b.getWidth(), b.getHeight());
RectF dst;
// if simpleMapping is good enough then remove "else" branch and
// declare "dst" as:
// RectF dst = new RectF(bounds);
boolean simpleMapping = true;
if (simpleMapping) {
dst = new RectF(bounds);
} else {
float x = bounds.exactCenterX();
dst = new RectF(x, 0, x, bounds.height());
float scale = bounds.height() / src.height();
float dx = scale * src.width() / 2;
dst.inset(-dx, 0);
}
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xaa00ff00);
canvas.drawBitmap(getBitmap(), mMatrix, null);
}
}
Solution 2
The solution in the answer is the best, to use it:
Resources res = getActivity().getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.some_image);
BackgroundBitmapDrawable background = new BackgroundBitmapDrawable(res, bitmap);
view.setBackgroundDrawable(background);
or
view.setBackground(background);
for API 16 and above.
Related videos on Youtube
user1987392
Updated on September 15, 2022Comments
-
user1987392 3 months
I'm trying to set a photo as the background of an
Activity
. I want the background to be a full screen image (no borders).As I want the image to fill the entire activity's background without stretching/squeezing (i.e. unproportional scalings for X and Y) and I don't mind if the photo has to be cropped, I'm using a
RelativeLayout
with anImageView
(withandroid:scaleType="centerCrop"
) and the rest of my layout consisting of aScrollView
and its children.<!-- Tried this with a FrameLayout as well... --> <RelativeLayout> <!-- Background --> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop"/> <!-- Form --> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView> <LinearLayout> ... <EditText/> </LinearLayout> </ScrollView> </LinearLayout> </RelativeLayout>
The problem is that the rest of the layout has some
EditText
views and when the softkeyboard shows up, the ImageView gets re-sized. I would like the background to remain the same, irregardless of whether the softkeyboard is visible or not.I have seen plenty of questions on SO about ImageViews being re-sized but (imo) no satisfactory answers. Most of them just consist of setting the
android:windowSoftInputMode="adjustPan"
- which is not always practical, especially if you want the user to be able to scroll in the activity - or usinggetWindow().setBackgroundDrawable()
which doesn't crop the image.I've managed to sub-class ImageView and override its
onMeasure()
(see my answer here: ImageView resizes when keyboard open) so that it can force a fixed height & width - equal to the device's screen dimensions - according to a flag but I'm wondering if there's a better way of achieving the result I want.So, to sum up, my question is: How can I set an Activity's background to be a full-screen photo
with scale type = "centerCrop", so that the photo is scaled uniformly (maintaining its aspect ratio) and therefore both dimensions (width and height) of will be equal to or larger than the corresponding dimension of the view;
that doesn't get resized when a softkeyboard pops up;
ANSWER:
I ended up following @pskink's advice and subclassed
BitmapDrawable
(see his answer bellow). I had to do some adjustments to make sure that theBackgroundBitmapDrawable
is always scaled and cropped in a way that fills the screen.Here's my final class, adapted from his answer:
public class BackgroundBitmapDrawable extends BitmapDrawable { private Matrix mMatrix = new Matrix(); private int moldHeight; boolean simpleMapping = false; public BackgroundBitmapDrawable(Resources res, Bitmap bitmap) { super(res, bitmap); } @Override protected void onBoundsChange(Rect bounds) { if (bounds.height() > moldHeight) { moldHeight = bounds.height(); Bitmap b = getBitmap(); RectF src = new RectF(0, 0, b.getWidth(), b.getHeight()); RectF dst; if (simpleMapping) { dst = new RectF(bounds); mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER); } else { // Full Screen Image -> Always scale and center-crop in order to fill the screen float dwidth = src.width(); float dheight = src.height(); float vwidth = bounds.width(); float vheight = bounds.height(); float scale; float dx = 0, dy = 0; if (dwidth * vheight > vwidth * dheight) { scale = (float) vheight / (float) dheight; dx = (vwidth - dwidth * scale) * 0.5f; } else { scale = (float) vwidth / (float) dwidth; dy = (vheight - dheight * scale) * 0.5f; } mMatrix.setScale(scale, scale); mMatrix.postTranslate(dx, dy); } } } @Override public void draw(Canvas canvas) { canvas.drawColor(0xaa00ff00); canvas.drawBitmap(getBitmap(), mMatrix, null); } }
Then its just a matter of creating a
BackgroundBitmapDrawable
and setting it as the root View's background.-
Jim almost 9 yearsI'm assuming you've tried all the stuff here (I think "isScrollContainter" solved my problem, but I can't find it now): stackoverflow.com/questions/4207880/…
-
Meanman over 8 yearsAmazing. This is the best solution, going to post some code below for usage.
-
user1987392 almost 9 yearsThat's exactly my problem. And I've tried both with a FrameLayout and RelativeLayout. The problem is that when the softkeyboard shows up, the ImageView in the background gets re-sized. Even though the ImageView maintains its proportions, I'd prefer if it maintained the same appearance whether the keyboard is hidden or shown.
-
user1987392 almost 9 yearsThat doesn't guarantee that the whole viewport will be filled with the image like in stackoverflow.com/questions/16135984/…. Wouldn't this solution allow a scenario where the image fills (for example) only half of the screen and the rest of the screen is filled by a solid color, depending on the orientation, screen density, size, etc.? That's not what I'm trying to achieve. See the link in this comment.
-
pskink almost 9 years@user1987392 you say no "stretching/squeezing" so it meams no scaling at all and now you say if screen is bigger than image then do image scaling?
-
user1987392 almost 9 yearsOk maybe my words weren't chosen wisely: by stretching/squeezing I meant the image not being re-sized with the same proportion for its height and width. That would obviously make the photo look weird. I do want scaling but I want it to be "center-crop".
-
Adnane.T almost 9 yearseven configuring it to crop?
-
user1987392 almost 9 yearsYes, that's the first thing I tried: Frame/RelativeLayout with the ImageView (centerCrop). The image would get re-sized when the keyboard was displayed. See the accepted answer + my edit :)
-
rupps over 8 yearsthis is terrible, you are using 2 viewgroups, even heavy layout weights, just to scale an image!
-
Huey over 7 yearsNot sure how this improves on the other answers. Anyway, why not add code to illustrate what you mean?