前面的Android应用中已经大量使用了简单图片,图片不仅可以使用ImageView来显示,也可作为Butto、Window的背景。从广义的角度来看,Android应用中的图片不仅包括*.png、*.jpg、*.gif等各种格式的位图,也包括使用XML资源定义的各种Drawable对象。
使用Drawable对象
为Android应用增加了Drawable资源之后,Android SDK会为这份资源在R清单文件中创建一个索引项:R.drawable.file_name。
接下来既可在XML资源文件中通过@drawable/file_name来访问该Drawable对象,也可在Java代码中通过R.drawable.file_name来访问该Drawable对象。
需要指出的是,R.drawable.file_name是一个int类型的常量,它只代表Drawable对象的ID,如果Java程序中需要获取实际的Drawable对象,既可调用Resources的getDrawable(int id)方法来获取。
Bitmap和BitmapFactory
Bitmap代表一张位图,BitmapDrawable里封装的图片就是一个Bitmap对象。开发者为了把一个Bitmap对象包装成BitmapDrawable对象,可以调用BitmapDrawable的构造器:
//把一个Bitmap对象包装成BitmapDrawable对象
BitmapDrawable drawable=new BitmapDrawable(bitmap);
如果需要获取BitmapDrawable所包装的Bitmap对象,则可调用BitmapDrawable的getBitmap()方法,如下面代码所示:
//获取一个BitmapDrawable所包装的Bitmap对象
Bitmap bitmap=drawable.getBitmap();
除此之外,Bitmap还提供了一些静态方法来创建新的Bitmap对象,例如如下常用方法。
- createBitmap(Bitmap source,int x,int y,int width,int height):从源位图source的指定坐标点(给定x、y)开始,从中“挖取”宽width、高height的一块出来,创建新的Bitmap对象。
- createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter):对源位图src进行缩放,缩放成宽dstWidth、高dstHeight的新位图。
- createBitmap(int width,int height,Bitmap.Config config):创建一个宽width、高height的新位图。
- createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter):从源位图source的指定坐标点(给定x,y)开始,从中“挖取”宽width、高height的一一块出来,创建新的Bitmap对象。并按Matrix指定的规则进行变换。
BitmapFactory是一个工具类,它用于提供大量的方法,这些方法可用于从不同的数据源来解析、创建Bitmap对象。BitmapFactory包含了如下方法。
- decodeByteArray(byte[] data,int offset,int length):从指定字节数组的offset位置开始,将长度为lenth的字节数组解析成Bitmap对象。
- decodeFile(String pathName):从pathName指定的文件中解析、创建Bitmap对象。
- decodeFileDescriptor(FileDescriptor fd):用于从FieDescriptor对应的文件中解析、创建Bitmap对象。
- decodeResource(Resource res,int id):用于根据给定的资源ID从指定的资源中解析、创建Bitmap对象。
- decodeStream(InputStream is):用于从指定输入流中解析、创建Bitmap对象。
大部分时候,我们只要把图片放在/res/drawable-mdpi目录下,就可以在程序中通过该图片对应的资源ID来获取封装该图片的Drawble对象。但由于手机系统的内存比较小,如果系统不停的去解析、创建Bitmap对象,可能由于前面创建Bitmap所占用的内存还没有回收,而导致程序运行时引发OutOfMemory错误。
Android为Bitmap提供了两个方法来判断他是否已回收,以及强制Bitmap回收自己。
- boolean isRecycled():返回该Bitmap对象是否已被回收。
- void recycle():强制一个Bitmap对象立即回收自己。
除此之外,如果Android应用需要访问其他存储路径(比如SD卡中)里的图片,都需要借助于BitmapFactory来解析、创建Bitmap对象。
下面开发一个查看/assets/目录下图片的图片查看器,该程序界面十分简单,只包含一个ImageView和一个按钮,但用户单击该按钮时程序会自动搜寻/assets/目录下的下一张图片。
界面布局代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:id="@+id/next" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下一张图片"/> <ImageView android:id="@+id/image" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scaleType="fitCenter"/> </LinearLayout>
该程序的代码如下:
package com.example.studydrawable; import java.io.IOException; import java.io.InputStream; import android.os.Bundle; import android.app.Activity; import android.content.res.AssetManager; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; public class BitmapTest extends Activity { String[] images=null; AssetManager assets=null; int currentImg=0; ImageView image; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bitmap_test); image=(ImageView)findViewById(R.id.image); try{ assets=getAssets(); //获取/assets/目录下所有文件 images=assets.list(""); } catch(IOException e) { e.printStackTrace(); } //获取bn按钮 final Button next=(Button)findViewById(R.id.next); //为bn按钮绑定事件监听器,该监听器将会查看下一张图片 next.setOnClickListener(new OnClickListener(){ @Override public void onClick(View source) { // TODO Auto-generated method stub //如果发生数组越界 if(currentImg>=images.length) { currentImg=0; } //找到下一张图片 while(!images[currentImg].endsWith(".png") &&!images[currentImg].endsWith(".jpg") &&!images[currentImg].endsWith(".gif")) { currentImg++; //如果已经发生数组越界 if(currentImg>=images.length) { currentImg=0; } } InputStream assetFile=null; try { //打开指定资源对应的输入流 assetFile=assets.open(images[currentImg++]); } catch(IOException e) { e.printStackTrace(); } BitmapDrawable bitmapDrawable=(BitmapDrawable)image.getDrawable(); //如果图片还未回收,先强制回收该图片 if(bitmapDrawable!=null&&!bitmapDrawable.getBitmap().isRecycled())//① { bitmapDrawable.getBitmap().recycle(); } //改变ImageView显示的图片 image.setImageBitmap(BitmapFactory.decodeStream(assetFile));//② }}); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.bitmap_test, menu); return true; } }
上面程序中①号粗体字代码用于判断当前ImageView所显示的图片是否已被回收,如果该图片还未回收,系统强制回收该图片;程序的②好粗体字代码调用了BitmpFactory从指定输入流解析、并创建Bitmap对象。