要好好整整不清楚的细节问题了~~加油!!!
虽然网上也有好多关于这方面的博客。。但是毕竟总结下来的才是自己的嘛。。所以我还是写一写我的总结吧。。
有不好不懂的地方还希望给留言指出一二哦。。谢谢啦。。共同学习~
这篇说的是加载大图片中的问题。。趁着自己的思路清晰赶紧写下来。。希望能帮助到和之前一样迷糊的同学。。
前言:
我们在新建一个虚拟机的时候,默认的虚拟机内存也就是VMHeap为16M,所以如果在加载的过程中图片超过了16M就会立马出现java.lang.OutOfMemoryError:bitmap size exceeds VMbudget的错误。
这里希望你明确两个不同的概念:
假如一张图片的像素为:3648*2736,其大小为2.1M;
其实:
加载一张图片时为其分配的内存大小与其图片本身的大小是不同的概念;
那么一张图片在加载时需要的内存是怎么计算的呢?
我们知道原始的图片都是位图,(百度的定义如下)
位图文件(Bitmap),扩展名可以是.
bmp或者.dib。
位图是Windows标准格式图形文件,它将
图像定义为由点(像素)组成,每个点可以由多种色彩表示,包括2、4、8、16、24和32位色彩。例如,一幅1024×768分辨率的32位真彩图片,其所占存储字节数为:1024×768×32/8=3072KB
打开Android.graphics.Bitmap类里有一个内部类Bitmap.Config类,在Bitmap类里createBitmap(int width, int height, Bitmap.Config config)方法里会用到
ARGB_4444、ARGB_8888和RGB_565
,打开个这个类一看:(这是参考的别人的话语:
http://blog.youkuaiyun.com/look85/article/details/8028157
)
枚举变量
public static final Bitmap.Config ALPHA_8
public static final Bitmap.Config ARGB_4444
public static final Bitmap.Config ARGB_8888
public static final Bitmap.Config RGB_565
那么ALPHA_8, ARGB_4444,ARGB_8888,RGB_565 到底是什么呢?
其实这都是色彩的存储方法:我们知道ARGB指的是一种色彩模式,里面A代表Alpha,R表示red,G表示green,B表示blue,其实所有的可见色都是右红绿蓝组成的,所以红绿蓝又称为三原色,每个原色都存储着所表示颜色的信息值
说白了就ALPHA_8就是Alpha由8位组成
ARGB_4444就是由4个4位组成即16位,
ARGB_8888就是由4个8位组成即32位,
RGB_565就是R为5位,G为6位,B为5位共16位
由此可见:
ALPHA_8 代表8位Alpha位图
ARGB_4444 代表16位ARGB位图
ARGB_8888 代表32位ARGB位图
RGB_565 代表16位RGB位图
位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真
回到正题:8位即8bit = 1 byte
所以: ARGB_4444 = 16bit/8 = 2bytes
ARGB_8888 = 32bit/8 = 4bytes
RGB_565
= 16bit/8 = 2bytes
所以图片在加载时需要的空间为:总像素的个数*像素的单位=3648*736=9980928*2bytes=19961856bytes/1024
=19494kB/1024 = 19.0371MB
在加载图片时由于是按照像素点来分配空间,所以加载图片时实际为其分配的内存空间为19MB大于了默认的16MB,内存不崩才怪呢!!!!!
可见加载图片时所需的内存大小与图片实际占有的大小是不一样的哦。
解决办法(先写一个,后期有更好的再补):
1,缩放加载
第一步:获取图片的宽和高,假如获取的图片的宽和高为:3648*2736
第二步:获取屏幕的宽和高320*480
第三步:计算缩放的比例:
宽度缩放比例:
3648/320 = 11;
高度缩放比例: 2736 /480 = 5;
第四步:比较宽和高的缩放比例:原则--哪一个大就用哪一个进行缩放
所以选择用比例11来进行缩放:
缩放之后图片的大小为:331*248;
再次计算加载时所需的内存大小为:160KB
远远减小了~~~
上代码:
package com.itheima40.loadbigpic;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.view.Display;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
public class MainActivity extends Activity {
private EditText etPath;
private ImageView ivIcon;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//加载控件---获取输入路径的文本框和显示图片的ImageView
etPath = (EditText) findViewById(R.id.et_path);
ivIcon = (ImageView) findViewById(R.id.iv_icon);
}
//按钮的点击事件
/**
* 缩放加载
* @param v
*/
public void scaleLoad(View v) {
String path = etPath.getText().toString();
Options opts = new Options();
opts.inJustDecodeBounds = true; // 设置为true, 加载器不会返回图片, 而是把Options对象中以out开头的字段给设置了.
BitmapFactory.decodeFile(path, opts);
// 得到了图片的宽和高
int imageWidth = opts.outWidth;
int imageHeight = opts.outHeight;
System.out.println("图片的宽和高: " + imageWidth + " * " + imageHeight);
// 获取屏幕的宽和高
Display display = this.getWindowManager().getDefaultDisplay(); // 获取默认窗体显示的对象
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
System.out.println("屏幕的宽和高: " + screenWidth + " * " + screenHeight);
// 计算缩放比例
int widthScale = imageWidth / screenWidth;
int heightScale = imageHeight / screenHeight;
//选择比例大的那个进行缩放
int scale = widthScale > heightScale ? widthScale:heightScale;
System.out.println("缩放比例: " + scale);
// 使用计算出来的比例进行缩放
opts.inJustDecodeBounds = false; // 指定加载可以加载出图片.
opts.inSampleSize = scale;
Bitmap bm = BitmapFactory.decodeFile(path, opts);
// 显示到ImageView控件上
ivIcon.setImageBitmap(bm);
}
}
main.xml中的样式,简单~不写后台的XML实现了---忘记说了。。图片是存放在sd卡上的。。随便拷贝一张图片到SDCard的目录下就行
代码中需要注意的地方:
opts
.
inJustDecodeBounds
=
true
;
// 设置为true, 加载器不会返回图片, 而是把Options对象中以out开头的字段给设置
所以你看
BitmapFactory
.
decodeFile
(
path
,
opts
);并没有返回值吧。
但是后期我们需要
BitmapFactory
.
decodeFile
(
path
,
opts
);返回一张图片的时候就将
opts
.
inJustDecodeBounds
=
false;
opts.inJustDecodeBounds = true;
// 设置为true, 加载器不会返回图片, 而是把Options对象中以out开头的字段给设置了.
BitmapFactory.decodeFile(path, opts);
上面两句可以不用写的~