BitMap代表一张位图,扩展名可以是.bmp或者.dib.
位图文件图像效果好,但是非压缩格式的,需要占用较大存储空间,不利于网络上传播。jpg格式则恰好弥补了位图文件这个缺点。
在android系统中,bitmap是图像处理最重要的类之一。用它可以获取图像文件信息,进行图像剪切、旋转、缩放等操作,并可以指定格式保存图像文件。
-
从资源文件中获取
Bitmap = rawBitmap = BitmapFactory.decodeResource(getResources(),R.drawble.img1);
-
从SD卡中得到图片
//方法1 String SDCardPath = Environment.getExternalStorageDirectory().toString(); String filePath = SDCardPath+"/"+"haha.jpg"; Bitmap rawBitmap1 = BitmapFactory.decodeFile(filePath,null); //方法2 InputStream inputStream = getBitmapInputStreamFromSDCard("haha.jpg"); Bitmap rawBitmap2 = BitmapFactory.decodeStream(inputStream);
Android图像处理之Bitmap类
本文介绍Bitmap的使用方法——用Bitmap实现图像文件的读取和写入,并用Bitmap实现图像的剪切、旋转和缩放变换。
Bitmap的生成
1.1 BitmapFactory decode出Bitmap
Bitmap实现在android.graphics包中,但是Bitmap类的构造函数是私有的,外面并不能实例化,只能是通过JNI实例化。这必然是某个辅助类提供了创建Bitmap的接口,而这个类的实现通过JNI接口来实例化Bitmap的,这个类就是BitmapFactory.
利用BitmapFactory可以从一个指定的文件中,利用decodeFile()解出Bitmap;也可以从定义的图片资源中,利用decodeResource()解出Bitmap。
1.2 decode时的选项
在使用方法decodeFile()/decodeResource()时,都可以指定一个BitmapFactory.Options.
利用Options的下列属性,可以指定decode的选项:
- inPreferredConfig 指定decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config中,缺省值是ARGB_8888.
- inJustDecodeBounds如果设置为ture,并不会把图像的数据完全解码,亦即decodeXyz()返回值为null,但是Options的outAbc中解出了图像的基本信息。
- inSampleSize设置decode时的缩放比例。
利用Options的这些值就可以高效的得到一幅缩略图。
先设置inJustDecodeBounds= true,调用decodeFile()得到图像的基本信息
利用图像的宽度(或者高度,或综合)以及目标的宽度,得到inSampleSize值,再设置inJustDecodeBounds= false,调用decodeFile()得到完整的图像数据
先获取比例,再读入数据,如果欲读入大比例缩小的图,将显著的节约内容资源。有时候还会读入大量的缩略图,这效果就更明显了。
利用Bitmap和Matrix实现图像变换
Bitmap可以和Matrix结合实现图像的剪切、旋转、缩放等操作。
用源Bitmap通过变换生成新的Bitmap的方法:
public static Bitmap createBitmap(Bitmap source, int x, int y, intwidth, int height,
Matrix m, boolean filter)
public static Bitmap createBitmap(Bitmap source, int x, int y, intwidth, int height)
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
int dstHeight,boolean filter)
第一个方法是最终的实现,后两种只是对第一种方法的封装。
第二个方法可以从源Bitmap中指定区域(x,y,width,height)中挖出一块来实现剪切;第三个方法可以把源Bitmap缩放为dstWidth x dstHeight的Bitmap.
设置Matrix的Rotate(通过setRotate())或者Scale(通过setScale()),传入第一个方法,可实现旋转或缩放。
保存图像文件
经过图像变换之后的Bitmap里的数据可以保存到图像压缩文件里(JPG/PNG).
这个操作过程中,Bitmap.compress()方法的参数format可设置JPEG或PNG格式;quality可选择压缩质量;fOut是输出流(OutputStream),这里的FileOutputStream是OutputStream的一个子类。
BitmapFactory.Options类使用
http://www.tuicool.com/articles/63emAv
android之BitmapFactory.Options的使用
BitmapFactory.Options的使用是在加载图片时,就从图片的加载和使用说起。
怎么获取图片大小?首先将图片转出Bitmap,然后再利用Bitmap的getWidth()和getHeight()方法就可以取到图片的宽高了。
在通过BitmapFactory.decodeFile(String path)方法将图片转成Bitmap时,遇到大一些的图片,经常回遇到OOM(Out Of Memory)的问题,如何避免呢?此时就需要用到BitmapFactory.Options这个类
BitmapFactory.Options这个类,有一个字段叫做inJustDecodeBounds.如果我们把它设置为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,仅仅返回它的宽和高。这样就不会占用太大内存,也就不会那么频繁的发生OOM了。
示例代码如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(path, options);/* 这里返回的bmp是null */
这段代码之后,options.outWidth和options.outHeight就是我们想要的宽和高了。
有了宽和高的信息,我们怎样在图片不变形的情况下获取到图片指定大小的缩略图呢?
inSampleSize 成员变量
inSamplesSize表示缩略图大小为原始图片的几分之一。若该值为3,则取出的缩略图的宽和高都是原始图片的1/3,图片大小就为原始图片的1/9.
另外,为了节约内存我们还可以使用下面的几个字段:
options.inDither=false; /*不进行图片抖动处理*/
options.inPreferredConfig=null; /*设置让解码器以最佳方式解码*/
---------
/* 下面两个字段需要组合使用 */
options.inPurgeable = true;
options.inInputShareable = true
Android Matrix
Android Matrix 理论与应用详解
http://blog.csdn.net/kuku20092009/article/details/6740865
Matrix
set...,post...,pre...是矩阵的三种变换形式
set是重新设置数组
post是后乘M' = SM(原数组在后)
pre是前乘M'=MS(原数组在前)
Android关于获取图片方向,调整显示方向
在图片复制过程中会使得图片的方向信心丢失,我们需要调整它的方向再生产Bitmap
ExifInterface图片文件信息的类
通过ExifInterface类读取图片文件的被旋转角度
Android 图片与旋转
http://blog.csdn.net/tao_zi7890/article/details/38712371
什么是Exif
Exif(Exchangeable Image File可交换图像文件)是一种图像文件格式,它的数据存储于JPEG格式是完全相同的,实际上Exif格式就是在JPEG格式头部插入了数码照片的信息。简单地说,Exif= JPEG+拍摄参数。
什么地方用到Exif信息呢?我遇到的至少有如下这么几个地方:
1.生成右上角缩略图;
2.图片显示应用,例如android自带的gallery3d应用;
3.图片回显;
4.短(彩)信等需要添加camera附件的应用.
查看源码:ImageManager中是这样读取Exif方向信息的。
public static int getExifOrientation(String filepath) {
int degree = 0;
ExifInterface exif = null;
try {
exif = new ExifInterface(filepath);
} catch (IOException ex) {
Log.e(TAG, "cannot read exif", ex);
}
if (exif != null) {
int orientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, -1);
if (orientation != -1) {
// We only recognize a subset of orientation tag values.
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree =180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
}
}
return degree;
}
在应用层对于Exif的操作是通过android.media.ExifInterface接口完成的。、通过public void setAttribute (String tag, String value) 来设置,而获取可以通过 public int getAttributeInt (String tag, int defaultValue) 和 public String getAttribute (String tag) 两种方法都可以,getAttributeInt 重载方法一第二个参数为我们设置的默认值,如果成功则返回相应Tag的值;特定的整数内容为该方法直接返回值。而重载方法二该方法直接返回结果,如果失败 则为null。
Android官方图片加载利器BitmapFun解析
本文说明不包括BitmapFun的缓存部分
Android之Bitmap大图加载处理
缩放解决了内存问题,但是还有一个问题,实际解析图片的时候是从硬盘读取的,当图片较大的时候这个解析的时间是不确定的,如果直接在UI线程中执行,会观测到明显的停顿,再时间长一点就ANR了,所以要将解析的过程放在异步操作了。可以单开一个线程,也可以使用系统提供的异步任务类AsynTask
Android开发在使用ListView和GridView时,可能会有很多网络图片需要加载,通常我们会为每个图片加载启动一个Thread或者直接使用官方提供的AsyncTask,来做Http异步加载,但当每个ImageView子视图都触发一个AsyncTask来异步加载图片时,会产生如下问题:
- 当用户快速滑动时,ImageView已经被回收,而绑定的线程还在运行,浪费CPU,浪费内存。
- 无法确保当前视图在结束时,分配的视图已经进入循环队列中给另外一个子视图进行重用,意思就是,图片显示错位了,不该显示到当前问题的图片却显示了,这个是经常遇到的问题。可以结合Adapter中的getView方法的convertView参数理解,ListView是回收和重复利用item的。
- 无法确保所有的异步任务能够按顺序执行
在这些问题下,官网给出的答案是,使用下面的方法来保证:
- ImageView和Task绑定准确的加载对应图片;
- ImageView和Task无法对应时则取消任务;
BitmapFun实现多线程并发加载图片的原理
一个类,两个方法:
class AsyncDrawable extends BitmapDrawable{}
boolean cancelPotentialWork(String url, ImageView imageView){}
ImageViewResizeTask getBitmapWorkerTask(ImageView imageView){}
具体代码实现如下:
/**
*扩展BitmapDrawable类,通过弱引用关联任务BitmapWorkerTask
* BitmapDrawable被用来作为占位图片,绑定任务到ImageView中
*/
private class AsyncDrawable extends BitmapDrawable(){
private final WeakReference viewResizeTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask viewResizeTask){
super(res, bitmap);
viewResizeTaskReference = new WeakReference(viewResizeTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return viewResizeTaskReference.get();
}
}
/**
* 确保ImageView执行的是它对应的Task,否则取消任务
* @param url
* @param imageView
* @return
*/
private boolean cancelPotentialWork(String url, ImageView imageView) {
// 获得ImageView对应的Task
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if
(bitmapWorkerTask != null) {
final String imgUrl = bitmapWorkerTask.url;
if (imgUrl == null || !imgUrl.equals(url)) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
}else{
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
/**
* 获得已经被分配到ImageView的指定的Task
* @param imageView
* @return
*/
private BitmapWorkerTask getBitmapWorkerTask(ImageView imageView {
if (imageView !=null) {
final Drawable drawable = mageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
使用方法:
/**
* 异步加载图片
* @param url
* @param imageView
*/
private void loadBitmap(String url, ImageView imageView) {
if (cancelPotentialWork(url, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(),null, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(url);
}
}
/*
* 异步加载图片Task类
*/
class
BitmapWorkerTask
extends
AsyncTask {
...
@Override
protected void onPostExecute(Bitmap bitmap)
if (isCancelled()) {
bitmap =null
}
if (imageViewReference != null && bitmap !=null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =getBitmapWorkerTask(imageView);
if ( this == bitmapWorkerTask && imageView != null ) {
imageView.setImageBitmap(bitmap);
}
}
}
}
最佳使用实践,提高流畅度
-
设置ListView的OnScrollListener事件,在滑动的时候不加载图片
public void onScrollStateChanged(AbsListView view, int scrollState) { if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { if(!Utils.hasHoneycomb()) { mImageFetcher.setPauseWork(true); } }else{ mImageFetcher.setPauseWork(false); } }
在Activity或Fragment的onResume(),onPause(),onDestroy()方法中调用恰当方法非常有用。
@Override
public void onResume() {
super.onResume();
mImageFetcher.setExitTasksEarly(false);
}
@Override
public void onPause() {
super.onPause();
mImageFetcher.setPauseWork(false);
mImageFetcher.setExitTasksEarly(true);
mImageFetcher.flushCache();
}
@Override
public void onDestroy() {
super.onDestroy();
mImageFetcher.closeCache();
}
Android 之Bitmap大图片加载处理
在Android中显示图片一般使用ImageView,通过setImageBitmap()、setImageResource()等方法指定要显示的图片,而这些方法最终都会调用BitmapFactory.decode()方法来生成一个Bitmap进行显示。对于一般的小图片这样使用没有什么问题,因为垃圾回收器会及时将不用的图片进行回收,但连续加载大图片的时候就会发生典型的OOM问题,在Android系统中虚拟机为每一位应用程序都分配了指定内存大小,如果使用超出了这个限制就会发生内存溢出导致程序崩溃。
为了避免OOM问题,需要对大图片的加载进行管理,主要通过缩放来减小图片的内存占用。
- Bitmap的缩放
生成Bitmap都用通过BitmapFactory的decode()方法,使用此方法时可以传入一个解析参数BitmapFactory.Options来对控制解析过程,比如获得长宽、改变采样率、改变采样格式等。
还有一个问题,就是实际解析图片的时候是从硬盘上读取的,当图片较大的时候这个解析时间是不确定的,如果直接在UI线程中执行,会观察到明显的停顿,时间再长一点就ANR了,所以需要将图片的解析过程放在异步操作了,可以单开一个线程,也可以使用系统的提供的异步任务类AsyncTask,示例如下:
class BitmapWorkerTask extends AsyncTask{
private final WeakReference imageViewReference;
private String data = null;
public BitmapWorkerTask(ImageView imageView){
//Use a WeakReference to ensure the ImageView can be garbege collected
imageViewReference = new WeakReference(imageView);
}
//Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
data = params[0];
return decodeSampledBitmapFromFile(data, 100, 100));
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
这里需要传入ImageView,当后台图片解析完成后就会将图片加载进行显示。
关于Bitmap图片的加载Google官网上有详细的教程讲解,除了解析还有缓存的解决方案
参考:
Android学习之位图BitMap
Android Bitmap详细介绍
Android技术精髓-Bitmap详解
http://blog.csdn.net/zhangtengyuan23/article/details/18818567?utm_source=tuicool&utm_medium=referral