前言
已经有一段时间没有些博客了,最经又把关于图像处理的这块内容有温习了一遍,总结一下吧!
效果如下
Bitmap
Bitmap代表一张位图,BitmapDrawable里面封装的图片就是一个Bitmap对象。开发者为了把一个Bitmap对象包装成BitmapDrawable对下,可以调用BitmapDrawable的构造器:
//获取一个BitmapDrawable所包装的Bitmap对象
Bitmap bit = drawable.getBitmap();
除此之外,Bitmap还提供一些静态方法来创建新的Bitmap对象,例如如下常用方法。
static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height)
Returns an immutable(不可变) bitmap from the specified subset of the source bitmap
意思是从原图的坐标点(给点的X,Y)开始,从中“挖取”宽width,高height的一块出来,创建新的Bitmap对象。static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)
Creates a new bitmap, scaled from an existing bitmap, when possible.
创建一个缩放的bitmap对象static Bitmap createBitmap(int[] colors, int width, int height, Bitmap.Config config)
Returns a immutable bitmap with the specified width and height, with each pixel value set to the corresponding value in the colors array.
创建一个指定宽高的bitmap对象static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
创建一张按照Matrix指定规则进行变换的bitmap对象
Android官方文档
BitmapFactory
BitmapFatory是一个工具类,他用于提供大量的方法,这些方法可以从不同的数据源来解析,创建Bitmap对象。BitmapFactory包含了如下方法。
Creates Bitmap objects from various sources, including files, streams, and byte-arrays.
static Bitmap decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts)
Decode an immutable bitmap from the specified byte array.
从指定字节数组的offset开始,将长度为length的字节数据解析成Bitmap对象static Bitmap decodeByteArray(byte[] data, int offset, int length)
Decode an immutable bitmap from the specified byte array.
从指定字节数组的offset开始,将长度为length的字节数据解析成Bitmap对象官方文档如下
How to use it?
Android为Bitmap提供两个方法来判断他是否已被回收了,以及强制Bitmap回收自己。
- Boolean isRecycled();返回该Bitmap对象是否已被回收
- void recycle();强制一个Bitmap对象立即回收自己
场景
自拟一个使用场景,在/assets/目录下面创建一个子目录imgs,然后再imgs下存放一些图片,然后做一个图片查看器,每点击一次图片就切换下一张。
注意:在assets目录下的资源是无法在R.java文件中创建索引的,所以要借助AssetManager 来获取资源
稍微了解一下AssetManager对象
//继承图如下
public final class
AssetManager
extends Object
java.lang.Object
↳ android.content.res.AssetManage
看一下官方的described info吧
Provides access to an application’s raw asset files; see Resources for the way most applications will want to retrieve their resource data. This class presents a lower-level API that allows you to open and read raw files that have been bundled with the application as a simple stream of bytes.
> 大概意思就是可以通过AssetManager获取应用程序原始的资源文件,之所以原始,是因为asset目录下的资源没有在R.java中进行资源登记,所以要不能在Context的Resource对象中获取到asset目录下的文件,而与此对应的R.java中登记过的资源,可以称之为非原始文件,可以通过resource对象获取之。
提供如下方法
除此之外我在另外一篇文章中也有对BitmapFactory有介绍 《加载大图避免出现OOM》
- public final String[] getLocales ()
Get the locales that this asset manager contains data for.
获取系统所支持的语言
- final String[] list(String path)
Return a String array of all the assets at the given path.
获取path路径下的资源文件名字符串数组
try {
strImagesArray = assetManager.list("images");
if(strImagesArray==null){
Log.i("info", "images is null");
}else{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < strImagesArray.length; i++) {
sb.append(strImagesArray[i]).append("\t");
}
Log.i("info", sb.toString());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i("info", e.toString());
}
![这里写图片描述](https://img-blog.csdn.net/20160404165141495) 获取到的数据是图片文件名
final InputStream open(String fileName)
Open an asset using ACCESS_STREAMING mode.
默认使用ACCESS_STREAMING模式获取文件流final InputStream open(String fileName, int accessMode)
四种Mode,有兴趣的自行研究一下
int ACCESS_BUFFER Mode for open(String, int): Attempt to load contents into memory, for fast small reads.
int ACCESS_RANDOM Mode for open(String, int): Read chunks, and seek forward and backward.
int ACCESS_STREAMING Mode for open(String, int): Read sequentially, with an occasional forward seek.
int ACCESS_UNKNOWN Mode for open(String, int): no specific information about how data will be accessed.
InputStream is = null;
try {
//一定注意完成的资源路径
is = assetManager.open("images/"+strImagesArray[currentIndex++]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("error", e.toString());
}
- final AssetFileDescriptor openFd(String fileName)
- final AssetFileDescriptor openNonAssetFd(String fileName)
- final AssetFileDescriptor openNonAssetFd(int cookie, String fileName)
以上三个方法返回至均是AssetFileDescriptor 对象
public class
AssetFileDescriptor
extends Object
implements Parcelable Closeable
java.lang.Object
↳ android.content.res.AssetFileDescriptor
File descriptor of an entry in the AssetManager. This provides your own opened FileDescriptor that can be used to read the data, as well as the offset and length of that entry’s data in the file.
实际意思就是在InputStream open(String fileName, int accessMode)此方法上做了一层包装,便且是只读类型的,从字面意思就可以明白AssetFileDescriptor使资源文件描述器,封装了文件一些属性信息的描述,如获取到文件的字节长度等信息
再瞧一瞧AssetFileDescriptor类是什么玩意儿吧!
可以清晰的知道AssetFileDescriptor就是文件的详细描述类
获取的时候这样使用
AssetFileDescriptor afd = null;
try {
afd = assetManager.openFd("images/"
+ strImagesArray[currentIndex++]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
怎样使用这个对象呢,如下所示
if(afd!=null){
iv.setImageBitmap(BitmapFactory.
decodeFileDescriptor(afd.getFileDescriptor()));
}
然而当你这样写时候你以为会如你所愿,可事实并不这样
AssetFileDescriptor afd = null;
try {
afd = assetManager.openFd("images/"
+ strImagesArray[currentIndex++]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取上一张图片,并将其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if (bd != null) {
Bitmap bitmap = bd.getBitmap();
if (bitmap != null) {
if (bitmap.isRecycled()) {
bitmap.recycle();// 对上一张图片进行回收
}
} else {
Log.i("info", "bitmap is null");
}
} else {
Log.i("info", "pre bitmap is null");
}
// iv.setImageBitmap(BitmapFactory.decodeStream(is));
if (afd != null) {
FileDescriptor fd = afd.getFileDescriptor();
if (fd != null) { // 不为空
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd); // 返回值为空
if (bitmap != null) { // 结果为空
iv.setImageBitmap(bitmap);
} else {
Log.i("info",
"bitmap is null" + "length="
+ afd.getDeclaredLength()); // 可以得到真实文件长度
}
} else {
Log.i("info", "fd is null");
}
} else {
Log.i("info", "afd is null");
}
我也没有弄明白,感觉是个Bug,百度了一下仅仅找到了一两篇相关文章,都是这样的问题,别人也没有解决。不过可以参考一下
(http://markmail.org/thread/qab6epxwgoilomm5)
http://osdir.com/ml/Android-Developers/2012-11/msg01251.html)
实在是太奇怪了
(不正确代码,可以研究一下)为空完整代码如下:
package com.example.android_bitmap_001_1;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import android.os.Bundle;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView iv;
private int currentIndex;
private String[] strImagesArray;
private String[] locals;
private AssetManager assetManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
assetManager = getAssets();
locals = assetManager.getLocales();
for (int i = 0; i < locals.length && locals != null; i++) {
Log.i("info", locals[i]);
}
try {
strImagesArray = assetManager.list("images");
if (strImagesArray == null) {
Log.i("info", "images is null");
} else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < strImagesArray.length; i++) {
sb.append(strImagesArray[i]).append("\t");
}
Log.i("info", sb.toString());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i("info", e.toString());
}
iv.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
// 1.每次加载不同的图片所以,currentIndex每次要变化,同时一定判断越界
// 2.加载的图片一定要判断后缀是否正确
// 3.加载图片并加一
if (currentIndex >= strImagesArray.length) {
currentIndex = 0;
}
// 如果下一个文件的后缀不是该图片,currentIndex要自加一
while (!strImagesArray[currentIndex].endsWith("png")
&& !strImagesArray[currentIndex].endsWith("jpg")
&& !strImagesArray[currentIndex].endsWith("gif")) {
currentIndex++;
if (currentIndex >= strImagesArray.length) {
currentIndex = 0;
}
}
// InputStream is = null;
// try {
// is = assetManager.open("images/"
// + strImagesArray[currentIndex++]);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// Log.e("error", e.toString());
// }
AssetFileDescriptor afd = null;
try {
afd = assetManager.openFd("images/"
+ strImagesArray[currentIndex++]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 获取上一张图片,并将其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if (bd != null) {
Bitmap bitmap = bd.getBitmap();
if (bitmap != null) {
if (bitmap.isRecycled()) {
bitmap.recycle();// 对上一张图片进行回收
}
} else {
Log.i("info", "bitmap is null");
}
} else {
Log.i("info", "pre bitmap is null");
}
// iv.setImageBitmap(BitmapFactory.decodeStream(is));
if (afd != null) {
FileDescriptor fd = afd.getFileDescriptor();
if (fd != null) { // 不为空
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd); // 返回值为空
if (bitmap != null) { // 结果为空
iv.setImageBitmap(bitmap);
} else {
Log.i("info",
"bitmap is null" + "length="
+ afd.getDeclaredLength()); // 可以得到真实文件长度
}
} else {
Log.i("info", "fd is null");
}
} else {
Log.i("info", "afd is null");
}
}
});
}
}
最后来来看Bitmap和BitmapFactory最基本的使用方法吧
//获取上一张图片,并将其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if(bd!=null){
Bitmap bitmap = bd.getBitmap();
if(bitmap!=null){
if(bitmap.isRecycled()){
bitmap.recycle();// 对上一张图片进行回收
}
}else{
Log.i("info", "bitmap is null");
}
}else{
Log.i("info", "pre bitmap is null");
}
iv.setImageBitmap(BitmapFactory.decodeStream(is));
(正确结果代码)最后完整代码如下
package com.example.android_bitmap_001_1;
import java.io.IOException;
import java.io.InputStream;
import android.os.Bundle;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
public class MainActivity extends Activity {
private ImageView iv;
private int currentIndex;
private String[] strImagesArray;
private String[] locals;
private AssetManager assetManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
assetManager = getAssets();
locals = assetManager.getLocales();
for (int i = 0; i < locals.length && locals != null; i++) {
Log.i("info", locals[i]);
}
try {
strImagesArray = assetManager.list("images");
if (strImagesArray == null) {
Log.i("info", "images is null");
} else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < strImagesArray.length; i++) {
sb.append(strImagesArray[i]).append("\t");
}
Log.i("info", sb.toString());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.i("info", e.toString());
}
iv.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
// 1.每次加载不同的图片所以,currentIndex每次要变化,同时一定判断越界
// 2.加载的图片一定要判断后缀是否正确
// 3.加载图片并加一
if (currentIndex >= strImagesArray.length) {
currentIndex = 0;
}
// 如果下一个文件的后缀不是该图片,currentIndex要自加一
while (!strImagesArray[currentIndex].endsWith("png")
&& !strImagesArray[currentIndex].endsWith("jpg")
&& !strImagesArray[currentIndex].endsWith("gif")) {
currentIndex++;
if (currentIndex >= strImagesArray.length) {
currentIndex = 0;
}
}
InputStream is = null;
try {
is = assetManager.open("images/"
+ strImagesArray[currentIndex++]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.e("error", e.toString());
}
// 获取上一张图片,并将其回收
BitmapDrawable bd = (BitmapDrawable) iv.getDrawable();
if (bd != null) {
Bitmap bitmap = bd.getBitmap();
if (bitmap != null) {
if (bitmap.isRecycled()) {
bitmap.recycle();// 对上一张图片进行回收
}
} else {
Log.i("info", "bitmap is null");
}
} else {
Log.i("info", "pre bitmap is null");
}
iv.setImageBitmap(BitmapFactory.decodeStream(is));
}
});
}
}