Andorid-如何为你的Android应用缩放图片
很难为你的应用程序得到正确的图像缩放吗?是你的图片过大,造成内存问题?还是图片不正确缩放造成不良用户体验的结果?为了寻求一个好的解决方案,我们咨询了Andreas Agvard(索尼爱立信软件部门),让他分享一些关于这方面的经验。
注意:本文没有完整显示出代码示例。你可以下载本文的PDF,来看完整的代码示例。
在索尼爱立信软件部门工作,我经常遇到需要图片缩放的应用,例如:当处理别人或者网络上提供的图片。缩放是必要的,因为通常情况下的图片不是你想要呈现的那样。
典型的例子,如果你正在为你的应用开发一个LiveView™扩展。大多数人开发应用利用LiveView™和其他第二屏幕设备,可能需要重新调整图片,重要的是要保持适当的缩放比例和图像质量。当然,在很多情况下,改变图片尺寸是一个有点困难,但是很有效的途径。
ImageView解决了许多的图片缩放问题,首先,至少你在设置完一个图片源后,不用去解码或缩放图片。但有时需要你自己去解码控制,这是本教程的用武之地。随着本教程,我写了一个代码示例,下载图片缩放代码示例。在文本中呈现的效果,可以通过编译和运行该项目来看到。
孤立的问题
我做这个教程,是因为我已经有一些实用方法来实现图片的缩放,为了避免最常见的图片缩放问题。如下面的例子:
Bitmap unscaledBitmap = BitmapFactory.decodeResource(getResources(), mSourceId);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(unscaledBitmap, wantedWidth, wantedHeight,
那么在上面的代码中,什么是正确的,什么是错的?让我们来看看在不同的代码行。
行1:整个源图像解码到一个位图。
- 这可能会导致内存不足的错误,如果图片太大的话。
- 这可能会导致在一个高分辨率上解码图像。这可能会很慢,但智能解码器可为解码提高性能。
- 缩放图片很多时候是,高分辨率位图缩放到低分辨率,会导致锯齿的问题。使用位图过滤(例如,通过传送`true`参数到Bitmap.createScaledBitmap(...))减少了锯齿,但是还是不够。
行2:解码的位图缩放到想要的大小。
- 源图像的尺寸和想要的图像尺寸在长宽比上可能是不一样的。这将导致图像的拉伸。
左边的图片:原始图像。右边的图片:缩放后图片。可以看出明显的失真问题,如原图的眼睛非常的鲜明,缩放后就没有了。高度出现拉伸。
创建一个解决方案
我们的解决方案,将有一个结构类似上述代码,其中的一部分将取代行1,这样为缩放做准备。另一部分将取代行2,做最后的缩放。我们将开始替换行2的部分代码,引入两个新的概念,裁剪和合适。
替换行2
在这一部分,我们将缩放位图到我们所需要的。这一步很必要,因为之前的解码能力是有限的。此外,在这一步为了避免拉伸,我们可能要重新调整图片到想要的大小。
有两种可能性可以避免拉伸。不管是那种,我们都要调整尺寸,以确保他们有相同的宽高比;即缩放图像作为源图像,直到它适合想要的尺寸,或裁剪具有相同的宽高比的源图像为想要的尺寸。
左边的图片:图像通过fit方法缩放。图片已被缩小到适合的尺寸和高度,结果是小于想要的高度。右边的图像:图像crop方法缩放。图像已被缩放到适应至少想要的尺寸。因此原图已被裁剪,切割了成左边和右边二部分。
为了缩放这样的效果,我们的实现代码如下:
public static Bitmap createScaledBitmap(Bitmap unscaledBitmap, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect, new Paint(Paint.FILTER_BITMAP_FLAG));return scaledBitmap;
}
在上面的代码,我们使用canvas.drawBitmap(...)做缩放。这种方法的裁剪区域是从源图像的规模面积定义画布的矩形为指定的目标矩形区域。为了避免拉伸,这两个矩形需要有相同的长宽比。我们还调用了两个实用的方法,一个为创建源矩形和另一个为创建目标矩形。方法如下:
public static Rect calculateSrcRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.CROP) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
final int srcRectWidth = (int)(srcHeight * dstAspect);
final int srcRectLeft = (srcWidth - srcRectWidth) / 2;
return new Rect(srcRectLeft, 0, srcRectLeft + srcRectWidth, srcHeight);
} else {
final int srcRectHeight = (int)(srcWidth / dstAspect);
final int scrRectTop = (int)(srcHeight - srcRectHeight) / 2;
return new Rect(0, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
}
} else {
return new Rect(0, 0, srcWidth, srcHeight);
}
}
public static Rect calculateDstRect(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return new Rect(0, 0, dstWidth, (int)(dstWidth / srcAspect));
} else {
return new Rect(0, 0, (int)(dstHeight * srcAspect), dstHeight);
}
} else {
return new Rect(0, 0, dstWidth, dstHeight);
}
}
在刚好合适的情况下源矩形会包含整个源尺寸。在需要裁剪的情况下,它会计算好具有相同宽高比的目标图像,来裁剪源图像的宽度或高度,以达到你想要的尺寸。而刚好在合适的情况下,将有相同宽高比的源图像,调整成你想要的尺寸的宽度或高度。
替换行1
解码器很智能,特别是用于JPEG和PNG的格式。这些解码器在图片解码时可以进行缩放,并且性能也有所改善,这样锯齿问题也可以避免。此外,由于图片解码后变小了,需要的内存也会较少。
缩放解码的时候,只要简单设置上BitmapFactory.Options对象的inSampleSize参数,并把它传递给BitmapFactory。样本大小指定一个缩放图像大小的抽象因素,例如2是640×480图像在320×240图像上解码的因素。样本大小设置时,你不能保证严格按照这个数字,图像将被缩减,但至少它不会更小。例如,3倍640×480的图像可能会导致在一个320×240图像不支持值。通常情况下,至少2的一次方支持[1,2,4,8,...]。
下一步是指定一个合适的样本大小。合适的样本大小将产生最大的缩放,但仍然是大于等于你想要的图像尺寸。如下面代码:
public static Bitmap decodeFile(String pathName, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic);
Bitmap unscaledBitmap = BitmapFactory.decodeFile(pathName, options);
return unscaledBitmap;
}
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight, ScalingLogic scalingLogic) {
if (scalingLogic == ScalingLogic.FIT) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
} else {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcHeight / dstHeight;
} else {
return srcWidth / dstWidth;
}
}
}
在decodeFile(...)方法中,我们解码一个文件进行了最终缩放尺度。这是首先要通过解码源图片尺寸,然后使用calculateSampleSize(...)计算最佳样本大小,最后使用此样本的大小解码图像。如果你有兴趣的话,你可以更深入了解calculateSampleSize(...)方法,但以上方法基本可确保图片进行缩放。
全部放在一起
根据上面我们指定的方法的,现在可以执行替换最初的代码行:
Bitmap unscaledBitmap = decodeFile(pathname, dstWidth, dstHeight, scalingLogic);
Bitmap scaledBitmap = createScaledBitmap(unscaledBitmap, dstWidth, dstHeight, scalingLogic);
左边的图像:原始解决方案,解码消耗6693 KB的内存和1/4秒左右。结果被拉长失真。中间的图像:同比缩放解决方案,解码消耗418 KB的内存和1/10秒左右。右边的图像:裁剪解决方案,解码消耗418 KB的内存和1/10秒左右。
想要了解更多信息,请下载我们的代码示例。有了这个源码项目,你可以看到你的Android手机上运行的结果。
Andorid-如何为你的Android应用缩放图片的更多相关文章
- Android动画及图片的缩放和旋转
Android动画有2种,一种是Tween Animation,另一种是Frame Animation,先说说Tween动画吧. Tween动画是对视图对象中的内容进行一系列简单的转换,比如位置的移动 ...
- Android仿微信图片上传,可以选择多张图片,缩放预览,拍照上传等
仿照微信,朋友圈分享图片功能 .可以进行图片的多张选择,拍照添加图片,以及进行图片的预览,预览时可以进行缩放,并且可以删除选中状态的图片 .很不错的源码,大家有需要可以下载看看 . 微信 微信 微信 ...
- Android拖动和缩放图片
Android拖动和缩放图片 2014年5月9日 我们在使用应用其中常常须要浏览图片.比方在微信其中.点击图片之后能够对图片进行缩放. 本博客介绍怎样对图片进行拖拽和缩放.这首先要了解Android中 ...
- 一起学习android图像缩放资源 (27)
效果图: 在平时载入图片时,我会使用SetImageBitmap.setImageResource.BitmapFactory.decodeResource来设置一张图 片通过以上方法来设置图片时.会 ...
- Android实现对图片的缩放、剪切、旋转、存储
转载:http://www.cnblogs.com/jerehedu/p/4464870.html 一.问题描述: 在开发中,当我们需要的有一张大图片同时还需要一些小图片时,我们只需要通过代码对此图片 ...
- Android 等比例缩放图片
// 缩放图片 public static Bitmap zoomImg(String img, int newWidth ,int newHeight){ // 图片源 Bitmap bm = Bi ...
- Android实现本地图片选择及预览缩放效果仿春雨医生
在做项目时常常会遇到选择本地图片的需求.曾经都是懒得写直接调用系统方法来选择图片.可是这样并不能实现多选效果.近期又遇到了,所以还是写一个demo好了.以后也方便使用.还是首先来看看效果 显示的图片使 ...
- 解决android:background背景图片被拉伸问题
ImageView中XML属性src和background的区别: background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸.src是图片内容(前 ...
- Android中的图片压缩
1.android中计算图片占用堆内存的kB大小跟图片本身的kB大小无关,而是根据图片的尺寸来计算的. 比如一张 480*320大小的图片占用的堆内存大小为: 480*320*4/1024=600kB ...
随机推荐
- properties配置应用,为什么需要使用properties文件
在项目中我们常常会使用Constants常量类,达到系统全局配置的目的. 但是有些常量需要动态的配置,如果项目上线后,每次修改Constants.java然后再编译,再上传Constants.clas ...
- POJ3764 The xor-longest path Trie树
代码写了不到30分钟,改它用了几个小时.先说题意,给你一颗树,边上有权,两点间的路径上的路径的边权抑或起来就是路径的xor值,要求的是最大的这样的路径是多少.讲到树上的两点的xor,一个常用的手段就是 ...
- Sublime Text 3配置与vim模式(待完整)
Sublime Text 3通过设置默认值与用户值的方式,来进行配置.默认值不允许更改,用户值是用户进行配置.同一属性,当用户值存在时,默认值就无效.打开Preference,如图: 先贴下我的Set ...
- 【转】12 款优秀的 JavaScript MVC 框架评估
JavaScript MVC 框架有很多,不同框架适合于不同项目需求.了解各种框架的性能及优劣有利于我们更加快捷的开发.作者(Gordon L.Hempton)一直在寻求哪种MVC框架最为完美,他将目 ...
- 为什么很多应用都安装在/usr/local目录下
为什么很多应用都安装在/usr/local目录下 很多应用都安装在/usr/local下面,那么,这些应用为什么选择这个目录呢?理解了最根源的原因后,也许对你理解linux组织文件的方式有更直观的 ...
- Java并发包中常用类小结(一)
从JDK1.5以后,Java为我们引入了一个并发包,用于解决实际开发中经常用到的并发问题,那我们今天就来简单看一下相关的一些常见类的使用情况. 1.ConcurrentHashMap Concurre ...
- GDB笔记
GDB是在Linux命令行下对C/C++的程序进行调试常用的一个命令,现将平时记录在本子上的笔记整理如下: 一.断点 断点类型有breakpoints, watchpoints, catchpoint ...
- *[hackerrank]ACM ICPC Team
https://www.hackerrank.com/contests/w6/challenges/acm-icpc-team 这道题在contest的时候数据量改小过,原来的数据量需要进行优化才能过 ...
- 【PHPsocket编程专题(实战篇③)】构建基于socket的HTTP请求类
该代码是两年前写的,现在看起来有点渣了,仅仅是提供一个思路,现在做一些Api开发的时候官方会有一些SDK,这些SDK其实原理都是通过socket来通讯的,其实我个人主张用curl更方便,当然前提是你的 ...
- QTimer源码分析(以Windows下实现为例)
QTimer源码分析(以Windows下实现为例) 分类: Qt2011-04-13 21:32 5026人阅读 评论(0) 收藏 举报 windowstimerqtoptimizationcallb ...