缩放图像以填充 ImageView 宽度并保持纵横比
Posted
技术标签:
【中文标题】缩放图像以填充 ImageView 宽度并保持纵横比【英文标题】:Scale Image to fill ImageView width and keep aspect ratio 【发布时间】:2013-08-07 06:59:25 【问题描述】:我有一个GridView
。 GridView
的数据是来自服务器的请求。
这是GridView
中的项目布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:background="@drawable/analysis_micon_bg"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/half_activity_vertical_margin"
android:paddingLeft="@dimen/half_activity_horizontal_margin"
android:paddingRight="@dimen/half_activity_horizontal_margin"
android:paddingTop="@dimen/half_activity_vertical_margin" >
<ImageView
android:id="@+id/ranking_prod_pic"
android:layout_
android:layout_
android:adjustViewBounds="true"
android:contentDescription="@string/app_name"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/ranking_rank_num"
android:layout_
android:layout_ />
<TextView
android:id="@+id/ranking_prod_num"
android:layout_
android:layout_ />
<TextView
android:id="@+id/ranking_prod_name"
android:layout_
android:layout_ />
</LinearLayout>
我从服务器请求数据,获取图片url并将图片加载到Bitmap
public static Bitmap loadBitmapFromInputStream(InputStream is)
return BitmapFactory.decodeStream(is);
public static Bitmap loadBitmapFromHttpUrl(String url)
try
return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
catch (Exception e)
Log.e(TAG, e.getMessage());
return null;
适配器中有getView(int position, View convertView, ViewGroup parent)
方法的代码
Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);
图片大小为210*210
。我在 Nexus 4 上运行我的应用程序。图像确实填充了 ImageView
宽度,但 ImageView
高度没有缩放。 ImageView
不显示整个图像。
我该如何解决这个问题?
【问题讨论】:
【参考方案1】:不使用任何自定义类或库:
<ImageView
android:id="@id/img"
android:layout_
android:layout_
android:adjustViewBounds="true"
android:scaleType="fitCenter" />
scaleType="fitCenter"
(省略时默认)
scaleType="centerInside"
src
的固有宽度小于父宽度将图像水平居中
如果 src
的固有宽度大于父宽度将使其与父宽度一样宽,并按比例缩小以保持纵横比。
使用android:src
或ImageView.setImage*
方法都没有关系,关键可能是adjustViewBounds
。
【讨论】:
android:layout_ android:adjustViewBounds="true" android:scaleType="fitCenter" 成功了 @JamesTanfill_parent
宽度只是一个好习惯,固定大小也可以。
@TWiStErRob 我意识到它需要 android:adjustViewBounds="true" ,否则它不适合高度。是的,适合父母的宽度是完整版本
在 API 19+ 上像魅力一样工作,但在 API 16 上却不行(经过测试)。 Alex Semeniuk 的自定义视图也适用于 API 16。
@F.Mysir whops ?,是的,不管是哪个问题,但对于传播良好实践,我完全同意。【参考方案2】:
我喜欢 arnefm 的回答,但他犯了一个小错误(请参阅 cmets),我将尝试纠正:
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
/**
* ImageView that keeps aspect ratio when scaled
*/
public class ScaleImageView extends ImageView
public ScaleImageView(Context context)
super(context);
public ScaleImageView(Context context, AttributeSet attrs)
super(context, attrs);
public ScaleImageView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
try
Drawable drawable = getDrawable();
if (drawable == null)
setMeasuredDimension(0, 0);
else
int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
if (measuredHeight == 0 && measuredWidth == 0) //Height and width set to wrap_content
setMeasuredDimension(measuredWidth, measuredHeight);
else if (measuredHeight == 0) //Height set to wrap_content
int width = measuredWidth;
int height = width * drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
setMeasuredDimension(width, height);
else if (measuredWidth == 0) //Width set to wrap_content
int height = measuredHeight;
int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
setMeasuredDimension(width, height);
else //Width and height are explicitly set (either to match_parent or to exact value)
setMeasuredDimension(measuredWidth, measuredHeight);
catch (Exception e)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
因此,如果(例如)放在 ScrollView
内部,您的 ImageView
将被正确缩放并且不会出现尺寸问题
【讨论】:
thx,我找到了解决方案,就在 arnefm 的回答下方,即我添加的第一条评论。there
是一个链接【参考方案3】:
我曾经遇到过类似的问题。我通过制作自定义 ImageView 解决了这个问题。
public class CustomImageView extends ImageView
然后覆盖imageview的onMeasure方法。我相信我做了这样的事情:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
try
Drawable drawable = getDrawable();
if (drawable == null)
setMeasuredDimension(0, 0);
else
float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
if (imageSideRatio >= viewSideRatio)
// Image is wider than the display (ratio)
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int)(width / imageSideRatio);
setMeasuredDimension(width, height);
else
// Image is taller than the display (ratio)
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = (int)(height * imageSideRatio);
setMeasuredDimension(width, height);
catch (Exception e)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
这将拉伸图像以适应屏幕,同时保持纵横比。
【讨论】:
见there。第一个答案 嗯,这个答案并不准确。考虑设置android:layout_height="wrap_content"
的情况。在这种情况下,MeasureSpec.getSize(heightMeasureSpec)
将是 0
,而您的 viewSideRatio
将导致 Infinity
(不用怀疑,Java 浮点数允许这样的值。)在这种情况下,您的 height
和 width
都将是 @ 987654331@.
对我来说,我需要将 (imageSideRatio >= viewSideRatio) 交换为 (imageSideRatio
【参考方案4】:
使用android:scaleType="centerCrop"
。
【讨论】:
不完全是问题的要求,但正是我需要的! 你能详细说明@Jameson吗?您是指 Android 版本吗? 如果我使用 centerCrop,图像会在顶部和底部被切掉一点。【参考方案5】:我做了与上述类似的事情,然后用头撞墙了几个小时,因为它在 RelativeLayout
内不起作用。我最终得到了以下代码:
package com.example;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class ScaledImageView extends ImageView
public ScaledImageView(final Context context, final AttributeSet attrs)
super(context, attrs);
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
final Drawable d = getDrawable();
if (d != null)
int width;
int height;
if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY)
height = MeasureSpec.getSize(heightMeasureSpec);
width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
else
width = MeasureSpec.getSize(widthMeasureSpec);
height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
setMeasuredDimension(width, height);
else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
然后为了防止RelativeLayout
忽略测量的尺寸,我这样做了:
<FrameLayout
android:id="@+id/image_frame"
android:layout_
android:layout_
android:layout_alignParentLeft="true"
android:layout_below="@+id/something">
<com.example.ScaledImageView
android:id="@+id/image"
android:layout_
android:layout_/>
</FrameLayout>
【讨论】:
【参考方案6】:FOR IMAGE VIEW(设置这些参数)
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:scaleType = "fitCenter"
android:adjustViewBounds = "true"
现在无论图像的大小如何,它的宽度都将与父级匹配,高度将根据比例匹配。我已经对此进行了测试,我 100% 确定。
我们想要这个 -->
不是这个 -->
// Results will be:
Image width -> stretched as match parent
Image height -> according to image width (maximum to aspect ratio)
// like the first one
【讨论】:
【参考方案7】:如果在ImageView中将图片设置为背景则不适用,需要在src(android:src)处设置。
谢谢。
【讨论】:
【参考方案8】:你不需要任何 java 代码。你只需要:
<ImageView
android:layout_
android:layout_
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
宽度和高度的键在匹配父级中
【讨论】:
centerCrop 实际上剪切了图像的某些部分。【参考方案9】:要创建一个宽度等于屏幕宽度,高度根据纵横比按比例设置的图像,请执行以下操作。
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>()
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation)
// creating the image that maintain aspect ratio with width of image is set to screenwidth.
int width = imageView.getMeasuredWidth();
int diw = resource.getWidth();
if (diw > 0)
int height = 0;
height = width * resource.getHeight() / diw;
resource = Bitmap.createScaledBitmap(resource, width, height, false);
imageView.setImageBitmap(resource);
);
希望这会有所帮助。
【讨论】:
这是我找到的最佳答案,我搜索了 2 天,谢谢 fathima【参考方案10】:在 ImageView 中使用这些属性来保持纵横比:
android:adjustViewBounds="true"
android:scaleType="fitXY"
【讨论】:
fitXY 不保持纵横比。它会拉伸图像以完全适合布局的宽度和高度。【参考方案11】:试试这个:它为我解决了问题
android:adjustViewBounds="true"
android:scaleType="fitXY"
【讨论】:
这个答案与***.com/a/37044123/1101730 有何不同?更重要的是这是一个错误的答案,因为 fitXY 不保持纵横比。【参考方案12】:尝试使用这个简单的行...在图像视图标记中的 xml 代码中添加此行,而不添加任何依赖项 android:scaleType="fitXY"
【讨论】:
fitXY 不保持纵横比,因此图像可能会被拉伸【参考方案13】:使用 android:ScaleType="fitXY" im ImageView xml
【讨论】:
fitXY 不保持纵横比,因此图像可能会被拉伸【参考方案14】:您可以尝试通过手动加载图像来完成您正在做的事情,但我强烈建议您查看Universal Image Loader。
我最近将它集成到我的项目中,我不得不说它太棒了。是否为您担心使事情异步、调整大小、缓存图像。集成和设置非常容易。在 5 分钟内,您可能可以让它为所欲为。
示例代码:
//ImageLoader config
DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();
if (ImageLoader.getInstance().isInited())
ImageLoader.getInstance().destroy();
ImageLoader.getInstance().init(config);
imageLoadingListener = new ImageLoadingListener()
@Override
public void onLoadingStarted(String s, View view)
@Override
public void onLoadingFailed(String s, View view, FailReason failReason)
ImageView imageView = (ImageView) view;
imageView.setImageResource(R.drawable.android);
Log.i("Failed to Load " + s, failReason.toString());
@Override
public void onLoadingComplete(String s, View view, Bitmap bitmap)
@Override
public void onLoadingCancelled(String s, View view)
;
//Imageloader usage
ImageView imageView = new ImageView(getApplicationContext());
if (orientation == 1)
imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
else
imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);
这可以延迟加载图像,使其适合 imageView 的大小,在加载时显示占位符图像,并在加载失败时显示默认图标并缓存资源。
-- 我还应该补充一点,这个当前配置保持图像纵横比,因此适用于您的原始问题
【讨论】:
【参考方案15】:只需使用 UniversalImageLoader 并设置
DisplayImageOptions.Builder()
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
.build();
ImageView 上没有缩放设置
【讨论】:
【参考方案16】:我遇到了类似的问题,我发现这是因为您需要计算dp
。当您从 drawable
加载时,Android Studio 正在计算 ImageView
,但是当您使用其他方法时,例如从 位图 加载时,dp
不会自动计算,
这是我的 xml
<ImageView
android:id="@+id/imageViewer"
android:layout_
android:layout_//dp is not automaticly updated, when loading from a other source
android:scaleType="fitCenter"
tools:srcCompat="@drawable/a8" />
我正在使用 Kotlin,并从资产文件加载可绘制对象,这是我的计算方法
val d = Drawable.createFromStream(assets.open("imageData/$imageName.png"), null)
bitHeight = d.minimumHeight//get the image height
imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
imageViewer.setImageDrawable(d)//set the image from the drawable
imageViewer.requestLayout()//here I apply it to the layout
【讨论】:
以上是关于缩放图像以填充 ImageView 宽度并保持纵横比的主要内容,如果未能解决你的问题,请参考以下文章
centerInside 不能将 Drawable 缩放到 ImageView
你如何缩放像 centerCrop 这样的 ImageView,但是从顶部开始?