Java Android:如何在保持纵横比的同时将图像拉伸到屏幕宽度?
我想下载一个图像(大小未知,但总是大致呈正方形),并将其显示为水平填充屏幕,垂直拉伸以在任何屏幕大小上保持图像的纵横比。这是我的(非工作)代码。它会水平拉伸图像,但不会垂直拉伸,因此会挤压图像Java Android:如何在保持纵横比的同时将图像拉伸到屏幕宽度?,java,android,layout,Java,Android,Layout,我想下载一个图像(大小未知,但总是大致呈正方形),并将其显示为水平填充屏幕,垂直拉伸以在任何屏幕大小上保持图像的纵横比。这是我的(非工作)代码。它会水平拉伸图像,但不会垂直拉伸,因此会挤压图像 ImageView mainImageView = new ImageView(context); mainImageView.setImageBitmap(mainImage); //downloaded from server mainImageView.setScaleType(Sc
ImageView mainImageView = new ImageView(context);
mainImageView.setImageBitmap(mainImage); //downloaded from server
mainImageView.setScaleType(ScaleType.FIT_XY);
//mainImageView.setAdjustViewBounds(true);
//with this line enabled, just scales image down
addView(mainImageView,new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
您正在将ScaleType设置为ScaleType.FIT_XY。根据,这将拉伸图像以适合整个区域,必要时更改纵横比。这可以解释你看到的行为
为了得到你想要的行为。。。FIT_CENTER、FIT_START或FIT_END接近,但如果图像的宽度小于其高度,则不会开始填充宽度。不过,您可以看看这些是如何实现的,并且您应该能够找到如何根据自己的目的对其进行调整。我通过自定义视图实现了这一点。设置layout\u width=“fill\u parent”和layout\u height=“wrap\u content”,并将其指向适当的可绘制位置:
public class Banner extends View {
private final Drawable logo;
public Banner(Context context) {
super(context);
logo = context.getResources().getDrawable(R.drawable.banner);
setBackgroundDrawable(logo);
}
public Banner(Context context, AttributeSet attrs) {
super(context, attrs);
logo = context.getResources().getDrawable(R.drawable.banner);
setBackgroundDrawable(logo);
}
public Banner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
logo = context.getResources().getDrawable(R.drawable.banner);
setBackgroundDrawable(logo);
}
@Override protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = width * logo.getIntrinsicHeight() / logo.getIntrinsicWidth();
setMeasuredDimension(width, height);
}
}
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class StretchableImageView extends ImageView{
public StretchableImageView(Context context) {
super(context);
}
public StretchableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StretchableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(getDrawable()!=null){
if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = width * getDrawable().getIntrinsicHeight()
/ getDrawable().getIntrinsicWidth();
setMeasuredDimension(width, height);
}else{
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = height * getDrawable().getIntrinsicWidth()
/ getDrawable().getIntrinsicHeight();
setMeasuredDimension(width, height);
}
}
}
}
最后,我手动生成了尺寸,效果非常好:
DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = width * mainImage.getHeight() / mainImage.getWidth(); //mainImage is the Bitmap I'm drawing
addView(mainImageView,new LinearLayout.LayoutParams(
width, height));
ScaleType.CENTER\u裁剪将执行您想要的操作:拉伸到全宽,并相应地缩放高度。如果缩放高度超过屏幕限制,图像将被裁剪。将adjustViewBounds设置为true并使用LinearLayout视图组对我来说效果非常好。无需子类化或询问设备指标:
//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);
我在线性布局中使用了这些值:
Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
多年来,我一直在以这样或那样的方式努力解决这个问题,谢谢,谢谢,谢谢……) 我只是想指出,通过扩展视图和覆盖onMeasure,您可以从Bob Lee所做的工作中得到一个可推广的解决方案。这样,您就可以将其用于任何您想要的绘图,如果没有图像,它也不会损坏:
public class CardImageView extends View {
public CardImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CardImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CardImageView(Context context) {
super(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable bg = getBackground();
if (bg != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = width * bg.getIntrinsicHeight() / bg.getIntrinsicWidth();
setMeasuredDimension(width,height);
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
我刚刚阅读了
ImageView
的源代码,如果不在这个线程中使用子类解决方案,这基本上是不可能的。在ImageView.onMeasure
中,我们可以看到以下几行:
// Get the max possible width given our constraints
widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);
// Get the max possible height given our constraints
heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);
其中h
和w
是图像的尺寸,p*
是填充
然后:
private int resolveAdjustedSize(int desiredSize, int maxSize,
int measureSpec) {
...
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
/* Parent says we can be as big as we want. Just don't be larger
than max size imposed on ourselves.
*/
result = Math.min(desiredSize, maxSize);
因此,如果您有一个layout\u height=“wrap\u content”
它将设置widthSize=w+pleft+pright
,或者换句话说,最大宽度等于图像宽度
这意味着,除非您设置精确的大小,否则图像永远不会放大。我认为这是一个bug,但祝你好运,让谷歌注意或修复它。strong>编辑:吃我自己的话,我提交了,他们说它已经在未来的版本中修复强>
另一个解决方案
这里是另一个子类解决方法,但您应该(理论上,我没有对它进行过太多测试!)能够在ImageView
的任何地方使用它。要使用它,请设置layout\u width=“match\u parent”
,以及layout\u height=“wrap\u content”
。它也比公认的解决方案要普遍得多。你既可以适合高度也可以适合宽度
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
// This works around the issue described here: http://stackoverflow.com/a/12675430/265521
public class StretchyImageView extends ImageView
{
public StretchyImageView(Context context)
{
super(context);
}
public StretchyImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public StretchyImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// Call super() so that resolveUri() is called.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// If there's no drawable we can just use the result from super.
if (getDrawable() == null)
return;
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int w = getDrawable().getIntrinsicWidth();
int h = getDrawable().getIntrinsicHeight();
if (w <= 0)
w = 1;
if (h <= 0)
h = 1;
// Desired aspect ratio of the view's contents (not including padding)
float desiredAspect = (float) w / (float) h;
// We are allowed to change the view's width
boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
// We are allowed to change the view's height
boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
int pleft = getPaddingLeft();
int pright = getPaddingRight();
int ptop = getPaddingTop();
int pbottom = getPaddingBottom();
// Get the sizes that ImageView decided on.
int widthSize = getMeasuredWidth();
int heightSize = getMeasuredHeight();
if (resizeWidth && !resizeHeight)
{
// Resize the width to the height, maintaining aspect ratio.
int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
setMeasuredDimension(newWidth, heightSize);
}
else if (resizeHeight && !resizeWidth)
{
int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;
setMeasuredDimension(widthSize, newHeight);
}
}
}
导入android.content.Context;
导入android.util.AttributeSet;
导入android.widget.ImageView;
//这可以解决此处描述的问题:http://stackoverflow.com/a/12675430/265521
公共类StretchImageView扩展了ImageView
{
公共StretchyImageView(上下文)
{
超级(上下文);
}
公共StretchImageView(上下文、属性集属性)
{
超级(上下文,attrs);
}
公共StretchyImageView(上下文上下文、属性集属性、int-defStyle)
{
超级(上下文、属性、定义样式);
}
@凌驾
测量时的保护空隙(内部宽度测量等级、内部高度测量等级)
{
//调用super()以便调用resolveUri()。
超级测量(宽度测量、高度测量);
//如果没有绘图功能,我们可以使用super的结果。
if(getDrawable()==null)
返回;
最终int-widthSpecMode=MeasureSpec.getMode(widthsmeasurespec);
最终int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
int w=getDrawable().getIntrinsicWidth();
int h=getDrawable().getIntrinsicHeight();
如果(w一个非常简单的解决方案是只使用RelativeLayout
提供的功能
以下是使标准Android视图成为可能的xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentBottom="true"
>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:text="button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<ImageView
android:src="@drawable/cat"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:layout_above="@id/button_container"/>
</RelativeLayout>
</ScrollView>
诀窍在于,你可以设置图像视图来填充屏幕,但它必须位于其他布局之上。这样你就可以实现你所需要的一切。对我来说,android:scaleType=“centerCrop”并没有解决我的问题。它实际上扩展了更多的图像。因此我尝试使用android:scaleType=“fitXY”它工作得非常出色。我仅使用此XML代码就成功地实现了这一点。可能是eclipse没有呈现高度以显示它扩展到合适的位置;但是,当您在设备上实际运行此功能时,它会正确地呈现并提供所需的结果。(至少对我来说是这样)
在某些情况下,这个神奇的公式完美地解决了问题
对于任何从另一个平台上挣扎的人来说,“尺寸和形状适合”选项在Android中处理得很好,但很难找到
您通常需要以下组合:
- 宽度匹配父级
- 高度包裹内容
- 调整视图边界已打开(原文如此)
- 比例fitCenter
- 克罗普托夫(sic)
然后,它是自动和惊人的
如果你是一个iOS开发人员,那么在Android中,你可以在一个表视图中实现“完全动态的单元格高度”,这真是太神奇了。呃,我是说ListView。享受吧
<com.parse.ParseImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/post_image"
android:src="@drawable/icon_192"
android:layout_margin="0dp"
android:cropToPadding="false"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:background="#eff2eb"/>
看,有一个更简单的解决方案:
ImageView imageView;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.your_layout);
imageView =(ImageView)findViewById(R.id.your_imageView);
Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image);
Point screenSize = new Point();
getWindowManager().getDefaultDisplay().getSize(screenSize);
Bitmap temp = Bitmap.createBitmap(screenSize.x, screenSize.x, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(temp);
canvas.drawBitmap(imageBitmap,null, new Rect(0,0,screenSize.x,screenSize.x), null);
imageView.setImageBitmap(temp);
}
android:adjustViewBounds="true"
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;
public class StretchableImageView extends ImageView{
public StretchableImageView(Context context) {
super(context);
}
public StretchableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StretchableImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(getDrawable()!=null){
if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = width * getDrawable().getIntrinsicHeight()
/ getDrawable().getIntrinsicWidth();
setMeasuredDimension(width, height);
}else{
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = height * getDrawable().getIntrinsicWidth()
/ getDrawable().getIntrinsicHeight();
setMeasuredDimension(width, height);
}
}
}
}
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/image"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
/>