在背景可绘制中保持图像纵横比的缩放

73

当使用<bitmap />作为背景的可绘制XML时,如何使背景图片适合视图但保持其纵横比? <bitmap>android:gravity值都不能达到预期效果。


1
你已经尝试过ScaleType了吗? - Chris
1
尝试使用android:scaleType="matrix" - Pavandroid
8
ScaleType是用于ImageView的,位图不支持它。 - OldSchool4664
9个回答

67

无法仅通过xml文件来操作背景属性。有两个选择:

  1. 你可以使用Bitmap.createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)在程序中对位图进行裁剪/缩放,并将其设置为某个视图的背景。

  2. 你可以使用ImageView代替背景,将它放置在第一个布局元素中,并为它指定android:scaleType属性:

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
    
            android:src="@drawable/backgrnd"
            android:scaleType="centerCrop" />
    
        ...
    
        rest layout components here
    
        ...
    
    </RelativeLayout>
    

3
所有可能的android:scaleType值都不能给出所需的效果。只有当我不指定scaleType时,图像才适应视图并保持其纵横比。 - tilex

36

从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>
唯一的缺点是,如果没有足够的空间,您的图像将无法完全显示,而是会被剪裁。我找不到一种直接从可绘制对象中完成这一操作的方法,但是从我所做的测试来看,它的效果非常好,而且不会过多地剪切图像。您可以尝试更多的重力选项。
另一种方式是创建一个布局,在该布局中使用ImageView并将scaleType设置为fitCenter
希望这些信息能帮助您实现想要的效果。

1
这将保持图像不缩放和小,并仅在底部显示它。 - J. Hesters
确实,如果您需要进行扩展,其他解决方案更合适。 正如您所看到的,这些属性没有涉及任何类型的扩展。 - Ionut Negru
5
这并没有回答这个问题。 - Firzen

5

我希望在自定义的Drawable类中实现类似的功能。以下是重要部分:

public class CustomBackgroundDrawable extends Drawable
{
    private Rect mTempRect = new Rect();
    private Paint mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    ...

public void draw(@NonNull Canvas canvas)
{
    Rect bounds = getBounds();
    if (mBitmap != null ) {
        if (mScaleType == ScaleType.SCALE_FILL) {
            //bitmap scales to fill the whole bounds area (bitmap can be cropped)
            if (bounds.height() > 0 && bounds.height() > 0) {
                float scale = Math.min(mBitmap.getWidth()/(float)bounds.width(), mBitmap.getHeight()/(float)bounds.height());

                float bitmapVisibleWidth = scale * bounds.width();
                float bitmapVisibleHeight = scale * bounds.height();

                mTempRect.set((int)(mBitmap.getWidth()-bitmapVisibleWidth)/2, 0, (int)(bitmapVisibleWidth+mBitmap.getWidth())/2, (int)bitmapVisibleHeight);
                canvas.drawBitmap(mBitmap, mTempRect, bounds, mBitmapPaint);
            }
        } else if (mScaleType == ScaleType.SCALE_FIT) {
            //bitmap scales to fit in bounds area
            if (bounds.height() > 0 && bounds.height() > 0) {
                float scale = Math.min((float)bounds.width()/mBitmap.getWidth(), (float)bounds.height()/mBitmap.getHeight());

                float bitmapScaledWidth = scale * mBitmap.getWidth();
                float bitmapScaledHeight = scale * mBitmap.getHeight();
                int centerPadding = (int)(bounds.width()-bitmapScaledWidth)/2;
                mTempRect.set(bounds.left + centerPadding, bounds.top, bounds.right - centerPadding, bounds.top+(int)bitmapScaledHeight);
                canvas.drawBitmap(mBitmap, null, mTempRect, mBitmapPaint);
            }
        }
    }
}

采用这种方法,您可以灵活地应用所需的任何比例逻辑


3

另一种方法是创建您的常规图像的九宫格拉伸图片,并使其按照您想要的比例进行缩放。

通过在角落处放置9个点,您可以将内容居中,并显然保持比例(假设您图像的最外侧是可重复/透明的)。

希望您能理解这个方法。


3
如果您的位图比高度更宽,请使用android:gravity="fill_vertical"。否则,请使用android:gravity="fill_horizontal"。这与在ImageView上使用android:scaleType="centerCrop"具有类似的效果。
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="fill_vertical"
    android:src="@drawable/image" />

如果您支持多个方向,您可以在 drawable-port 文件夹中创建一个位图 XML 文件,另一个在 drawable-land 文件夹中创建。


2
很遗憾,它不适用于 android:background - Dmitry

2

1
这是一些关于每种类型的示例,您可以在此处查看:http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html - Father Stack

2

尝试使用InsetDrawable(对我来说效果很好)。

只需将您的可绘制对象和您想要从四个方向之一插入(或填充)的插图提供给它即可。

InsetDrawable insetDrawable = new InsetDrawable(drawable, insetLeft, insetTop, insetRight, insetBottom);

这是用于设置背景可绘制的特定方法,大小比视图小。

参见此处: https://developer.android.com/reference/android/graphics/drawable/InsetDrawable.html


2

虽然这是一个老问题,但其他答案对我都不起作用。不过,以下的xml代码可以解决问题:

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:scaleType="centerCrop"
        android:src="@drawable/background_image"/> 

</RelativeLayout>

0

为了将图像适应可用空间(或者如果你已经设置了dp单位的宽度和高度),我尝试了以下方法,但只适用于图像不太宽的情况。

对于正方形图像,我将宽度和高度设置为相同值[或者你可以在两者上使用wrap_content]。

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_alignParentTop="true"
        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        android:src="@drawable/background_image"/> 

</RelativeLayout>

adjust view boundsscale type center fit就能解决问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接