Java 如何拥有不调整大小的全屏背景图像(带有中心裁剪)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21813432/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:48:45  来源:igfitidea点击:

How to have a Fullscreen Background Image (with center-crop) that doesn't Resize

javaandroidandroid-layoutandroid-imageviewbackground-drawable

提问by user1987392

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

我正在尝试将照片设置为Activity. 我希望背景是全屏图像(无边框)

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 RelativeLayoutwith an ImageView(with android:scaleType="centerCrop") and the rest of my layout consisting of a ScrollViewand its children.

由于我希望图像在不拉伸/挤压的情况下填充整个活动的背景(即 X 和 Y 的不成比例缩放),并且我不介意是否必须裁剪照片,因此我使用了RelativeLayoutwith an ImageView(with android:scaleType="centerCrop") 和我的布局的其余部分由 aScrollView及其子项组成。

<!-- 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 EditTextviews 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.

问题是布局的其余部分有一些EditText视图,当软键盘出现时,ImageView 会重新调整大小。无论软键盘是否可见,我都希望背景保持不变。

I have seen plenty of questionson 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 using getWindow().setBackgroundDrawable()which doesn't crop the image.

我在 SO 上看到了很多关于重新调整 ImageViews 大小的问题,但 (imo) 没有令人满意的答案。它们中的大多数只包括设置android:windowSoftInputMode="adjustPan"- 这并不总是实用的,特别是如果您希望用户能够在活动中滚动 - 或者使用getWindow().setBackgroundDrawable()不裁剪图像。

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.

我已经设法将 ImageView 子类化并覆盖它onMeasure()(请参阅我的答案:ImageView resizes when keyboard open),以便它可以根据标志强制固定高度和宽度 - 等于设备的屏幕尺寸 - 但我是想知道是否有更好的方法来实现我想要的结果。

So, to sum up, my question is: How can I set an Activity's background to be a full-screen photo

所以,总而言之,我的问题是:如何将 Activity 的背景设置为全屏照片

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

  • 使用 scale type = "centerCrop",使照片均匀缩放(保持其纵横比),因此两个尺寸(宽度和高度)将等于或大于视图的相应尺寸;

  • 弹出软键盘时不会调整大小;



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 the BackgroundBitmapDrawableis always scaled and cropped in a way that fills the screen.

我最终遵循了@pskink的建议并进行了子类化BitmapDrawable(见下面他的回答)。我不得不做一些调整以确保BackgroundBitmapDrawable始终以填充屏幕的方式缩放和裁剪。

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 BackgroundBitmapDrawableand setting it as the root View's background.

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

采纳答案by pskink

try this LayerDrawable (res/drawable/backlayer.xml):

试试这个 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"

并将其设置为您的顶级布局: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:

更新:试试这个 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);
    }
}

回答by Adnane.T

Put everything in a FrameLayout, with first child an imageview which is going to match it's parent (the frameLayout) and you configure it as you want your background, and in front of the image view you put whatever you want (LinearLayout I suppose)

把所有东西都放在一个 FrameLayout 中,第一个孩子是一个图像视图,它将匹配它的父元素(frameLayout),然后根据需要配置它作为背景,然后在图像视图前面放置任何你想要的东西(我想是 LinearLayout)

回答by user1987392

After scratching my head for a while this has been my "good for now" solution.

在挠了一阵子之后,这一直是我的“现在很好”的解决方案。

Sharing a sample XML that might be useful for anyone.

共享可能对任何人有用的示例 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>

With this layout, I accomplish two things:

通过这种布局,我完成了两件事:

  • formScrollView, which contains the "form" with the EditTexts, etc. gets re-sized (as I want it to) and scrollable when the softkeyboard shows up.

  • backgroundScrollView, which contains the ImageView, doesn't get re-sized (because of the android:isScrollContainer="false") and fills the entire viewport (android:fillViewport="true"). Thus the background remains the same irregardless of whether the keyboard is visible or not.

  • formScrollView,其中包含带有 EditTexts 的“表单”等。当软键盘出现时,它会重新调整大小(如我所愿)并可滚动。

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

Not a perfect solution as the ImageViewsomehow suffers a very small variation in its size, but the result is good enough for now. If someone knows a better solution, please let me know.

不是一个完美的解决方案,因为ImageView不知何故它的大小变化很小,但现在结果已经足够好了。如果有人知道更好的解决方案,请告诉我。

回答by Meanman

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.

对于 API 16 及更高版本。

回答by Kjk

Create an ImageView in your xml. Place it below your Layout tag. Set the translationZ to a positive value say 10dp and the scaleType to centerCrop.

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

回答by Kushal Ramola

Change your ScaleType

更改您的 ScaleType

 android:scaleType="FitXY"

and in your manifest

并在您的清单中

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