如何设置全屏背景图片(带中央裁剪),并且不改变其大小

19
我正在尝试将照片设置为Activity的背景。 我希望背景是全屏图像(无边框)
由于我希望图像填满整个活动背景而不拉伸/压缩(即X和Y的不成比例缩放),并且如果必须裁剪照片,我正在使用RelativeLayoutImageView(具有android:scaleType ="centerCrop")和其余布局包括ScrollView及其子项。
<!-- 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>

问题在于布局中还有一些EditText视图,当软键盘弹出时,ImageView会被重新调整大小。我希望背景保持不变,无论软键盘是否可见。
我在SO上看到很多问题关于ImageView被重新调整大小,但(在我看来)没有令人满意的答案。它们大多数只是设置android:windowSoftInputMode="adjustPan" - 这并不总是实用的,特别是如果您希望用户能够在活动中滚动 - 或使用getWindow().setBackgroundDrawable(),这不会裁剪图像。
我已经成功地子类化了ImageView,并重写了它的onMeasure()(请参见我的答案:ImageView resizes when keyboard open),以便根据标志强制设定固定的高度和宽度 - 等于设备的屏幕尺寸,但我想知道是否有更好的方法来实现我想要的结果。
所以,总的来说,我的问题是:如何将Activity的背景设置为全屏照片
  • 使用scale type = "centerCrop",使得照片等比例缩放(保持其宽高比),因此宽度和高度都将等于或大于视图的相应尺寸;

  • 当软键盘弹出时不会被重新调整大小;


答案:

最终我采用了@pskink的建议,子类化了BitmapDrawable(请参见他下面的回答)。我必须进行一些调整,以确保BackgroundBitmapDrawable始终按照填充屏幕的方式进行缩放和裁剪。

这是我的最终类,改编自他的回答:

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

然后,只需创建一个BackgroundBitmapDrawable并将其设置为根视图的背景即可。

你能发布一下你的XML吗?这听起来像是你想让活动的背景不要缩放(这可能是一个相对布局中的imageView,其中包含一个scrollView,其中包含其他元素),但你正在覆盖imageView,然后提到了EditText。你的XML可能有助于澄清问题。 - Jim
@Jim 我已经发布了一些XML。 重写ImageView只是我考虑过的一种可能的解决方案,但如果可以的话,我宁愿避免使用它。 - user1987392
不要使用ImageView,而应该使用适当的BitmapDrawable(带有顶部或中心重力)作为您的布局背景。 - pskink
1
我假设你已经尝试了这里的所有方法(我认为"isScrollContainter"解决了我的问题,但现在我找不到它):https://dev59.com/_W855IYBdhLWcg3wq2XL - Jim
1
太棒了。这是最好的解决方案,下面会发布一些代码供使用。 - Meanman
显示剩余8条评论
6个回答

7

尝试使用这个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>

将其设置为顶层布局:android:background="@drawable/backlayer"

更新:尝试使用BitmapDrawadle,将其设置为顶层布局(setBackgroundDrawable()),如果simpleMapping == true足够好,则可以删除“else”分支:

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

这并不能保证整个视口都会像https://dev59.com/EmQo5IYBdhLWcg3wVuKP中那样被图像填满。难道这种解决方案不会出现这样的情况:图像只填充屏幕的一半,其余部分由纯色填充,具体取决于方向、屏幕密度、大小等吗?这不是我想要实现的效果。请参见此评论中的链接。 - user1987392
@user1987392,你刚才说不进行“拉伸/压缩”,这意味着根本不进行缩放,现在你又说如果屏幕比图像大,则进行图像缩放? - pskink
好的,也许我选择的词不太好:通过"拉伸/压缩",我的意思是图像的高度和宽度不能按相同的比例重新调整大小。那样显然会使照片看起来很奇怪。我确实想要缩放,但我希望它是"中心裁剪"。 - user1987392

1

答案中的解决方案是最好的,使用它:

Resources res = getActivity().getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.some_image);
BackgroundBitmapDrawable background = new BackgroundBitmapDrawable(res, bitmap);
view.setBackgroundDrawable(background);

或者

view.setBackground(background);

适用于API 16及以上。


0

在思考了一段时间后,这是我目前的“临时解决方案”。

分享一个可能对任何人有用的示例XML。

<!-- RelativeLayout so that Views can overlap each other.
     I believe a FrameLayout would work as well. -->
<RelativeLayout
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- I've put the ImageView inside 
         a ScrollView with android:fillViewport="true" and android:isScrollContainer="false" -->
    <ScrollView
        android:id="@+id/backgroundScrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:isScrollContainer="false" > 

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical" >

            <!-- The FullScreen, Center-Cropped Image Background --> 
            <ImageView
                android:id="@+id/backgroundImage"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@drawable/background_image" />
        </LinearLayout>
    </ScrollView>

    <!-- The rest of the Views, containing the data entry form... -->
    <LinearLayout
        android:id="@+id/form"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

            <ScrollView
                android:id="@+id/formScrollView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" >

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="0dp"
                    android:layout_weight="1"
                    android:orientation="vertical" >

                    <!-- The "Form", with EditTexts, Checkboxes and whatnot... -->

                </LinearLayout>
            </ScrollView>

    </LinearLayout>
</RelativeLayout>

通过这种布局,我实现了两个目标:

  • formScrollView 包含“表单”中的 EditText 等控件,当软键盘弹出时,可以重新调整大小(按照我的要求)并可滚动。

  • backgroundScrollView 包含 ImageView,由于 android:isScrollContainer="false",它不会被重新调整大小,并填充整个视口 (android:fillViewport="true")。因此,无论键盘是否可见,背景始终保持不变。

这不是一个完美的解决方案,因为 ImageView 的大小会略微变化,但结果已经足够好了。如果有更好的解决方案,请告诉我。


真的太糟糕了,您使用了2个ViewGroup,甚至还有大量的布局权重,只是为了缩放一张图片! - rupps

0
将所有内容放在FrameLayout中,第一个子元素是ImageView,它将匹配其父元素(即FrameLayout),您可以根据需要配置其为背景。在ImageView之前,您可以放置任何您想要的内容(我想应该是LinearLayout)。

这正是我的问题。我已经尝试过使用FrameLayout和RelativeLayout。问题在于当软键盘出现时,背景中的ImageView会被重新调整大小。即使ImageView保持其比例,但我希望它的外观在键盘显示或隐藏时保持不变。 - user1987392
甚至配置它进行裁剪? - Adnane.T
是的,那是我尝试的第一件事:使用Frame/RelativeLayout和ImageView(centerCrop)。当键盘显示时,图像会被重新调整大小。请参见已接受的答案+我的编辑 :) - user1987392

-1

更改您的比例类型

 android:scaleType="FitXY"

并且在你的清单文件中

<activity
           android:name=".activity.MainActivity"
            android:windowSoftInputMode="adjustPan|stateHidden" />

-2
在你的xml中创建一个ImageView。 将它放置在Layout标签下面。 将translationZ设置为正值,比如10dp,并将scaleType设置为centerCrop。

不确定这个回答如何改进其他答案。无论如何,为什么不添加代码来说明你的意思呢? - Huey

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