如何拥有不调整大小的全屏背景图像(带中心裁剪)
Posted
技术标签:
【中文标题】如何拥有不调整大小的全屏背景图像(带中心裁剪)【英文标题】:How to have a Fullscreen Background Image (with center-crop) that doesn't Resize 【发布时间】:2014-03-15 19:48:31 【问题描述】:我正在尝试将照片设置为Activity
的背景。我希望背景是a full screen image (no borders)。
因为我希望图像填充整个活动的背景而不拉伸/挤压(即 X 和 Y 的不成比例缩放),并且我不介意是否必须裁剪照片,所以我使用 RelativeLayout
一个ImageView
(和android:scaleType="centerCrop"
)和我的布局的其余部分由一个ScrollView
和它的孩子组成。
<!-- Tried this with a FrameLayout as well... -->
<RelativeLayout>
<!-- Background -->
<ImageView
android:layout_
android:layout_
android:scaleType="centerCrop"/>
<!-- Form -->
<LinearLayout
android:layout_
android:layout_>
<ScrollView>
<LinearLayout>
...
<EditText/>
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
问题是布局的其余部分有一些EditText
视图,当软键盘出现时,ImageView 会重新调整大小。无论软键盘是否可见,我都希望背景保持不变。
我在 SO 上看到 plenty of questions 关于 ImageViews 正在重新调整大小但(imo)没有令人满意的答案。它们中的大多数只包括设置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 和包含其他元素的滚动视图),但是您正在覆盖 imageview,然后提到 EditText。您的 XML 可能有助于澄清问题。 @Jim 我已经发布了一些 XML。覆盖 ImageView 只是我考虑过的一种可能的解决方案,但如果可以的话,我宁愿避免它。 不要使用 ImageView,使用适当的 BitmapDrawable(顶部或中心重心)作为布局背景 我假设你已经尝试了这里的所有东西(我认为“isScrollContainter”解决了我的问题,但我现在找不到它):***.com/questions/4207880/… 太棒了。这是最好的解决方案,下面贴一些代码供大家使用。 【参考方案1】:将所有内容放在 FrameLayout 中,第一个子项是一个与其父级(frameLayout)匹配的图像视图,您可以根据需要对其进行配置,然后在图像视图前面放置您想要的任何内容(LinearLayout I假设)
【讨论】:
这正是我的问题。我已经尝试过使用 FrameLayout 和 RelativeLayout。问题是当软键盘出现时,后台的 ImageView 会重新调整大小。即使 ImageView 保持它的比例,我更喜欢不管键盘是隐藏还是显示它都保持相同的外观。 甚至将其配置为裁剪? 是的,这是我尝试的第一件事:带有 ImageView (centerCrop) 的 Frame/RelativeLayout。显示键盘时,图像会重新调整大小。查看接受的答案+我的编辑:)【参考方案2】:试试这个 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);
【讨论】:
这并不能保证整个视口都会像***.com/questions/16135984/… 中那样被图像填充。此解决方案是否允许图像仅填充(例如)屏幕的一半而屏幕的其余部分由纯色填充,具体取决于方向、屏幕密度、大小等?这不是我想要达到的目标。请参阅此评论中的链接。 @user1987392 你说没有“拉伸/挤压”,所以它根本没有缩放,现在你说如果屏幕大于图像然后进行图像缩放? 好吧,也许我的话没有明智地选择:通过拉伸/挤压,我的意思是图像的高度和宽度没有以相同的比例重新调整大小。这显然会使照片看起来很奇怪。我确实想要缩放,但我希望它是“中心裁剪”。【参考方案3】:在摸索了一段时间后,这是我的“暂时不错”的解决方案。
分享一个对任何人都可能有用的示例 XML。
<!-- RelativeLayout so that Views can overlap each other.
I believe a FrameLayout would work as well. -->
<RelativeLayout
android:id="@+id/root"
android:layout_
android:layout_ >
<!-- I've put the ImageView inside
a ScrollView with android:fillViewport="true" and android:isScrollContainer="false" -->
<ScrollView
android:id="@+id/backgroundScrollView"
android:layout_
android:layout_
android:fillViewport="true"
android:isScrollContainer="false" >
<LinearLayout
android:layout_
android:layout_
android:orientation="vertical" >
<!-- The FullScreen, Center-Cropped Image Background -->
<ImageView
android:id="@+id/backgroundImage"
android:layout_
android:layout_
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_
android:layout_
android:orientation="vertical" >
<ScrollView
android:id="@+id/formScrollView"
android:layout_
android:layout_ >
<LinearLayout
android:layout_
android:layout_
android:layout_weight="1"
android:orientation="vertical" >
<!-- The "Form", with EditTexts, Checkboxes and whatnot... -->
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
通过这个布局,我完成了两件事:
formScrollView
,其中包含带有 EditTexts 等的“表单”。当软键盘出现时,它会重新调整大小(如我所愿)并可以滚动。
包含 ImageView 的backgroundScrollView
不会重新调整大小(因为android:isScrollContainer="false"
)并填充整个视口(@987654326@)。因此,无论键盘是否可见,背景都保持不变。
不是一个完美的解决方案,因为 ImageView
的大小变化很小,但目前结果已经足够好了。如果有人知道更好的解决方案,请告诉我。
【讨论】:
这太糟糕了,您使用 2 个视图组,甚至是重布局权重,只是为了缩放图像!【参考方案4】:答案中的解决方案是最好的,使用它:
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 及更高版本。
【讨论】:
【参考方案5】:在您的 xml 中创建一个 ImageView。 将其放在您的布局标签下方。 将 translationZ 设置为正值,例如 10dp,将 scaleType 设置为 centerCrop。
【讨论】:
不确定这对其他答案有何改进。无论如何,为什么不添加代码来说明您的意思?【参考方案6】:更改您的 ScaleType
android:scaleType="FitXY"
在你的清单中
<activity
android:name=".activity.MainActivity"
android:windowSoftInputMode="adjustPan|stateHidden" />
【讨论】:
以上是关于如何拥有不调整大小的全屏背景图像(带中心裁剪)的主要内容,如果未能解决你的问题,请参考以下文章