在上一节学习 Drawable 图像资源的时候我们在很多地方用到了 bitmap,bitmap 其实就是真实图片在 Android 中最直接的表现形式,这一节我们来仔细学习一下 Bitmap 的使用。
1. 什么是 Bitmap
Bitmap 在 Android 中对应一张图片文件,它是一个二位系统,通过编码记录了一张图片的完整形式。以左上角为原点,向右和向下建立一个(X , Y)坐标系,坐标系中的每一个点都成为一个“像素”。在不同编码格式的 Bitmap 里一个像素占的 bit 数有所不同,这些 bit 共同表征了当前像素的色值,可能是8 bit、16 bit 或者 24 bit 等等,最后将这些所有的色值组合起来就成了一张完整的原始图片。
我们在 Android 中绘制的一切图像都是一个 Bitmap,我们可以创建一个 Bitmap 示例或者使用 Bitamp 工具来修改、优化一个图像资源。
2. Bitmap 的常用 API
- createBitmap(int width, int height, Bitmap.Config config):
- 根据传入的宽高、创建一个可修改的 Bitmap 对象
- createBitmap(DisplayMetrics display, int width, int height, Bitmap.Config config):
- 相比上一个接口,这个可以在创建的时候传入一些参数
- createBitmap(Bitmap src):
- 根据传入的 Bitamp 创建一个新的 Bitamp
- copy(Bitmap.Config config, boolean isMutable):
- 将 Bitamp 对象的所有像素复制到一个新的 Bitmap 当中
- extractAlpha():
- 提取原始 Bitmap 的透明度并返回一个新的 Bitamp
- getConfig():
- 获取 Bitamp 的配置信息
- getDensity():
- 返回 Bitamp 的图片像素密度
- getRowBytes():
- 返回 Bitamp 图片的像素字节数组
- setPixel(int x, int y, int color):
- 设置图片的(x, y)坐标点上的色值
- setDensity(int density):
- 设置图片像素密度
3. 获取 Bitamp 实例
Android 提供了多种方法获取 Bitamp 实例,我们可以直接从 ImageView 上拿到当前设置的图片的 Bitamp 对象:
private Bitmap bmp; private ImageView img; img = (ImageView)findViewById(R.id.imageView); BitmapDrawable drawable = (BitmapDrawable)img.getDrawable(); bmp = drawable.getBitmap();
从上面的代码中也可以看到我们可以通过 Drawable 的getBitmap()
方法从 Drawable 对象中提取出 Bitamp。
Android 还提供了一个 BitampFractory 工厂对象,专门让我们去获取 Bitmap 实例,主要有以下几种常用方法:
// 从资源文件中解码出 Bitmap private Bitmap getBitmapFromResource(Resources res, int resId) { return BitmapFactory.decodeResource(res, resId); } // 从图片文件中获取 private Bitmap getBitmapFromFile(String pathName) { return BitmapFactory.decodeFile(pathName); } // 从像素数组中获取 public Bitmap Bytes2Bimap(byte[] b) { if (b.length != 0) { return BitmapFactory.decodeByteArray(b, 0, b.length); } else { return null; } } // 读取输入流 private Bitmap getBitmapFromStream(InputStream inputStream) { return BitmapFactory.decodeStream(inputStream); }
4. Bitmap 使用示例
通过这两节的学习,我们知道 Drawable 和 Bitmap 可以相互转换,接下来编写一个示例完成将 ImageView 中的 Drawable 对象保存到本地的示例程序。
4.1 MainActivity 截图功能
package com.emercy.myapplication; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Canvas; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends Activity { static ByteArrayOutputStream byteOut = null; private Bitmap bitmap = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn_cut = (Button) findViewById(R.id.button); btn_cut.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { capture(); } }); } public void capture() { Runnable action = new Runnable() { @Override public void run() { final View contentView = getWindow().getDecorView(); try { bitmap = Bitmap.createBitmap(contentView.getWidth(), contentView.getHeight(), Bitmap.Config.ALPHA_8); contentView.draw(new Canvas(bitmap)); ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteOut); save(bitmap); } catch (Exception e) { e.printStackTrace(); } finally { try { if (null != byteOut) byteOut.close(); if (null != bitmap && !bitmap.isRecycled()) { bitmap = null; } } catch (IOException e) { e.printStackTrace(); } } } }; try { action.run(); } catch (Exception e) { e.printStackTrace(); } } private void save(Bitmap b) { FileOutputStream fos; try { fos = new FileOutputStream("sdcard/short.png"); boolean success = b.compress(Bitmap.CompressFormat.PNG, 80, fos); fos.flush(); fos.close(); if (success) { Toast.makeText(MainActivity.this, "截图完成", Toast.LENGTH_SHORT).show(); } } catch (IOException e) { e.printStackTrace(); } } }
在capture()方法中,我们获取当前 Activity 的“DecorView”(Activtiy 的顶层 View,我们设置的 CotentView 是其子 View),然后获取 DecorView 的输入流并会转化成 Bitmap,最后直接输出到文件中即可。
4.2 布局文件
这一节的布局文件可以任意,你可以尝试写出各种复杂的 UI 样式,因为上面的capture()方法是从“DecorView”中获取 Bitmap,所以无论你写了多少 View,最终都会通过setContentView()设置到“ContentView”中,而 ContentView 也是在 DecorView 中的,所以无论怎么写都逃离不开截图范围,最后记得加一个 Button 用于触发截屏。
5. 小结
本节进一步讲述了 Andorid 图像相关的内容,相比 Drawable,Bitmap 直接对应一张图片,更加具体。它可以与 Drawable 相互转化,并提供了多种 API 来直接操作一张图片,在做图片裁剪和修改的场景非常适用。