Android:如何缩放Drawable或背景图片?

144

在布局中,我想将背景图片按比例缩放以适应页面创建时分配的空间。有没有人有这样做的想法?

我正在使用layout.setBackgroundDrawable()BitmapDrawable()来设置gravity以进行裁剪和填充,但是没有看到任何缩放选项。


我也遇到了这个问题,并按照Dweebo的建议进行了更改,同时采纳了Anke的建议:将fitXY更改为fitCenter。效果很好。 - user475280
13个回答

93

要自定义背景图像的缩放,请创建如下资源:

<?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" />

如果将其用作背景,则它将居中于视图中。还有其他标志:http://developer.android.com/guide/topics/resources/drawable-resource.html


7
我尝试使用位图,但图像只是居中显示,而没有按照 Sam 的要求进行缩放。当我使用 ImageView 时,它非常好用,没有任何麻烦。 (注意:在我的情况下,我也没有滚动。)为了缩放图像,我应该如何修改位图? - Joe D'Andrea
7
可能的取值包括:"top"(顶部)|"bottom"(底部)|"left"(左侧)|"right"(右侧)|"center_vertical"(纵向居中)| "fill_vertical"(纵向填充)|"center_horizontal"(横向居中)|"fill_horizontal"(横向填充)| "center"(居中)|"fill"(填充)|"clip_vertical"(纵向裁剪)|"clip_horizontal"(横向裁剪) - Aleks N.
42
这些参数都不能按比例缩放位图,因为它们会保持其纵横比,所以这个答案是错误的。 - Malachiasz
7
亲爱的Malachiasz,'center'会遵守纵横比,但它不会缩小图像。这意味着使用这个功能有一定的风险。根据目标分辨率,你永远不知道缩放的结果会是多少。这是谷歌提供的另一个不太完美的解决方案。 - user458577
2
有没有一种方法可以添加scaleType属性? - ademar111190
显示剩余3条评论

58

我没有尝试过完全按照你想要的方式进行操作,但是你可以使用 android:scaleType="fitXY" 来缩放 ImageView ,
然后将其大小调整到 ImageView 给定的任何大小。

因此,您可以为布局创建一个 FrameLayout,将 ImageView 放在其中,然后将其他需要添加到 FrameLayout 的内容也放置在其中,设置为透明背景。

<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>

1
这个能在Surface上实现吗?如果不行,我能用FrameLayout来显示预览(录音应用)吗? - Namratha
1
@dweebo:你试过这个吗?对我来说似乎不起作用。 - speedplane
3
因为在诸如 ListView 这样繁忙的组件中使用两个 UI 组件而不是一个是个坏主意,所以我将您的回答评为负分。很可能在滚动时会遇到麻烦。正确的解决方案请参考:https://dev59.com/jnM_5IYBdhLWcg3wZSE6#9362168。 - Aleks N.
1
我尝试了fitCenter和centerCrop两种scaleType(针对不同密度的一些变化版本的drawable/image),它们都有效!任何一种缩放类型都适合我 - 我猜这更多是基于图像的个人偏好。请注意,我使用的是merge而不是FrameLayout,并且在我的特定情况下没有滚动。 - Joe D'Andrea
2
centerInside 是正确的,不要使用 fitXY,因为 fitXY 不能保持图像的纵横比。 - Malachiasz
显示剩余2条评论

41

从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。

2
这个解决方案绝对是我长期寻找的,以替换主题的背景属性并避免图像在视图有选项卡或没有选项卡时缩放不同。感谢分享这个。 - Bibu
1
它显示错误:错误:<item>必须具有“名称”属性。 - Dmitry

23

使用图像作为背景并根据布局大小进行调整:

<?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>

谢谢!同时 android:scaleType="centerCrop" 也可以使用。 - CoolMind

18
为保持比例,必须使用android:scaleType=fitCenterfitStart等。使用fitXY将无法保持图像的原始纵横比!请注意,此方法仅适用于具有src属性的图片,而不适用于背景图片。

5

当您使用setBackgroundDrawable方法设置ImageView的Drawable时,图像将始终被缩放。参数如adjustViewBounds或不同的ScaleTypes将被忽略。我发现保持宽高比的唯一解决方案是在加载Drawable后调整ImageView的大小。以下是我使用的代码片段:

// 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);

在以上代码片段中,bmp是要显示的Bitmap对象,imageViewImageView对象。
需要注意的一点是布局参数的更改。这是必要的,因为setMaxHeightsetMaxWidth只有在宽度和高度被定义为包裹内容时才会起作用,而不是填充父容器。另一方面,填充父容器是最初的期望设置,否则containerWidthcontainerHeight都将等于0。
因此,在您的布局文件中,您的ImageView将类似于以下内容:
...
<ImageView android:id="@+id/my_image_view"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
...

4
这不是最高效的解决方案,但是有人建议可以创建FrameLayout或RelativeLayout,并将ImageView用作伪背景-其他元素将简单地放在其上方:
<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>

ImageView存在的问题是只有以下几种scaleType可用: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)。
在某些情况下,需要“缩放背景图像(保持其宽高比)”,例如当您想要一个图像填充整个屏幕(例如背景图像),而屏幕的宽高比与图像不同时,必须使用一种类似于TOP_CROP的scaleType。因为:
CENTER_CROP将缩放后的图像居中显示,而不是将顶部边缘与图像视图的顶部边缘对齐;FIT_START适应屏幕高度而不填充宽度。正如用户Anke所指出的那样,FIT_XY不能保持宽高比。
值得庆幸的是,有人扩展了ImageView以支持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://dev59.com/q2w15IYBdhLWcg3w4_7k#14815588

现在,如果有人编写自定义Drawable来缩放图像,那么IMHO就会非常完美。然后它可以用作背景参数。

Reflog建议在使用drawable之前对其进行预缩放。以下是如何执行此操作的说明: Java(Android):如何在不使用位图的情况下缩放可绘制对象? 尽管它有缺点,即放大的drawable /位图将使用更多RAM,而ImageView使用的即时缩放不需要更多内存。优点可能是较少的处理器负载。


不错!完全符合我的要求:通过按比例增加宽度并裁剪图像底部来填充整个屏幕的背景图像。 - CMash

4
以下代码可使位图与ImageView的大小完美匹配。获取位图图像的高度和宽度,然后使用ImageView的参数计算出新的高度和宽度。这将为您提供最佳纵横比的所需图像。
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)

3
Dweebo提出的方法是可行的,但在我看来是不必要的。背景绘制本身可以很好地自适应缩放。视图应该有固定的宽度和高度,就像以下示例中所示:
 < 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>

2
我的理解是,背景会扩展以填充视图,但不会缩小。 - Edward Falk

1

尝试的一个选项是将图像放置在drawable-nodpi文件夹中,并将布局的背景设置为可绘制资源ID。

这肯定适用于缩小,但我还没有测试过放大。


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