一,仅拍照
首先先给大家看效果图:
点击拍照,拍照完成然后把照片显示在ImageView中:
1,拍照功能需要首先在AndroidManifest.xml添加SD卡读写权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />2, 在给大家看一下主布局中的代码:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.admin.phone.MainActivity"> <Button android:id="@+id/btn_ok" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="只是拍照" android:textSize="30px" /> <ImageView android:id="@+id/image_result" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
3,接下来看一下Acticity中的代码:
大家应该都可以看懂,其中imageUri 使用来保存拍完照片后存储的数据:
private static final int RESULT_CAMERA_ONLY = 0; private Button btn_ok; private ImageView mImage; private Uri imageUri;4,接下来再看OnCreate()中的代码:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String path = getSDCardPath(); File file = new File(path + "/image.jpg"); imageUri = Uri.fromFile(file); mImage = (ImageView) findViewById(R.id.image_result); btn_ok = (Button) findViewById(R.id.btn_ok); btn_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Photograph(); } }); }这里面做做了两部分:
1 .初始化了控件
可能大家下面这块代码看不太明白
imageUri = Uri.fromFile(file);其中的getSDCardPath();是自己写的函数,接下来会给出代码:
先构造一个image.jpg的URI
File file = new File(path + "/image.jpg");
/** * 其中的获取SD卡路径的代码为: * * @return */ public static String getSDCardPath() { String cmd = "cat /proc/mounts"; Runtime run = Runtime.getRuntime();// 返回与当前 Java 应用程序相关的运行时对象 try { Process p = run.exec(cmd);// 启动另一个进程来执行命令 BufferedInputStream in = new BufferedInputStream(p.getInputStream()); BufferedReader inBr = new BufferedReader(new InputStreamReader(in)); String lineStr; while ((lineStr = inBr.readLine()) != null) { // 获得命令执行后在控制台的输出信息 if (lineStr.contains("sdcard") && lineStr.contains(".android_secure")) { String[] strArray = lineStr.split(" "); if (strArray != null && strArray.length >= 5) { String result = strArray[1].replace("/.android_secure", ""); return result; } } // 检查命令是否执行失败。 if (p.waitFor() != 0 && p.exitValue() == 1) { // p.exitValue()==0表示正常结束,1:非正常结束 } } inBr.close(); in.close(); } catch (Exception e) { return Environment.getExternalStorageDirectory().getPath(); } return Environment.getExternalStorageDirectory().getPath(); }5.接下来会调用手机照相功能,首先先给大家扩充一下知识:
Exta Options Table for image/* crop:
附加选项 | 数据类型 | 描述 |
crop | String | 发送裁剪信号 |
aspectX | int | X方向上的比例 |
aspectY | int | Y方向上的比例 |
outputX | int | 裁剪区的宽 |
outputY | int | 裁剪区的高 |
scale | boolean | 是否保留比例 |
return-data | boolean | 是否将数据保留在Bitmap中返回 |
data | Parcelable | 相应的Bitmap数据 |
circleCrop | String | 圆形裁剪区域? |
MediaStore.EXTRA_OUTPUT ("output") | URI | 将URI指向相应的file:///...,详见代码示例 |
outputFormat | String | 输出格式,一般设为Bitmap格式:Bitmap.CompressFormat.JPEG.toString() |
noFaceDetection | boolean | 是否取消人脸识别功能 |
这里的参数可以选择性的使用;
在这里我们要使用把return-data 这个键始终设置为false;因为你如果将它设置为true的话,它会把始终拍到的照片显示出来为缩列图,上面的这些代码就是用到哪些功能,自己添加就好了,调起拍照就需要用到上面这些功能,接下来就给大家看~
/** * 点击按钮时调起拍照Intent,将结果存在imageUri中 */ private void Photograph() { Intent intent = null; intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//action is capture intent.putExtra("return-data", false); //是否将数据保留在Bitmap中返回 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将URI指向相应的file: intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//输出格式,一般设为Bitmap格式 intent.putExtra("noFaceDetection", true);//是否取消人脸识别功能 startActivityForResult(intent, RESULT_CAMERA_ONLY); }Intent传值就不给大家讲了,因为这个比较简单
调用照片功能就通过上面这些代码调起,代码中的备注也已经写好了。
6.拍完照片以后然后再对返回的数据进行处理。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK)
return;
switch (requestCode) {
case RESULT_CAMERA_ONLY: {
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
mImage.setImageBitmap(bitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
break;
}
}
这样的话有时候会报出OOM。 说到这里,其实我是想给大家截图一下报的OOM的代码,可惜我用真机在此测试的时候神奇的它居然不报OOM(尴尬).
这里出来OOM 我也是百度了一下:Android拍照,内存溢出OutOfMemory问题。
接下来再说一下加载位图原理分析。
1、BitmapFactory提供了几种解码方式(decodeByteArray(), decodeFile(), decodeResource()等等),以便从多种资源中创建一个Bitmap(位图)对象。可以根据你的图片数据来源选择最合适的解码方式。这些方法视图为构造Bitmap对象分配内存,因此很容易导致OutOfMemory(OOM)异常。每一种解码方式都有额外的特征,你可以通过BitmapFactory.Options类类指定解码方法。
2、尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource直接使用图片路径来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再调用上述方法将其设为ImageView的 source。decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。下面是使用InputStream加载图片的几种方法:
方法一、加载资源文件中指定的图片
InputStream is = getResources().openRawResource(R.drawable.temp);
方法二、加载assest目录下的图片
AssetManager asm=getAssetMg();
InputStream is=asm.open(name);//name:图片的名称
方法三、加载SD卡目录下的图片
String path =Environment.getExternalStorageDirectory().toString()+ "/DCIM/device.png";
inputStream is = new FileInputStream(path)
七、解决方案
private ImageView preview;
//1.加载位图
String path = Environment.getExternalStorageDirectory().toString()+"/DCIM/device.png";
inputStream is = new FileInputStream(path)
//2.为位图设置100K的缓存
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inTempStorage = new byte[100 * 1024];
//3.设置位图颜色显示优化方式
//ALPHA_8:每个像素占用1byte内存(8位)
//ARGB_4444:每个像素占用2byte内存(16位)
//ARGB_8888:每个像素占用4byte内存(32位)
//RGB_565:每个像素占用2byte内存(16位)
//Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存//也最大。也就意味着一个像素点占用4个字节的内存。我们来做一个简单的计算题:3200*2400*4 bytes //=30M。如此惊人的数字!哪怕生命周期超不过10s,Android也不会答应的。
opts.inPreferredConfig = Bitmap.Config.RGB_565;
//4.设置图片可以被回收,创建Bitmap用于存储Pixel的内存空间在系统内存不足时可以被回收
opts.inPurgeable = true;
//5.设置位图缩放比例
//width,hight设为原来的四分一(该参数请使用2的整数倍),这也减小了位图占用的内存大小;例如,一张//分辨率为2048*1536px的图像使用inSampleSize值为4的设置来解码,产生的Bitmap大小约为//512*384px。相较于完整图片占用12M的内存,这种方式只需0.75M内存(假设Bitmap配置为//ARGB_8888)。
opts.inSampleSize = 4;
//6.设置解码位图的尺寸信息
opts.inInputShareable = true;
//7.解码位图
Bitmap btp =BitmapFactory.decodeStream(is,null, opts);
//8.显示位图
preview.setImageBitmap(bitmap);
这里我是通过上述解释解决了位图OOM问题的。然后接下来再给大家看一下我修改后的代码:
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != Activity.RESULT_OK) return; switch (requestCode) { case RESULT_CAMERA_ONLY: { try { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inTempStorage = new byte[100 * 1024]; opts.inPreferredConfig = Bitmap.Config.RGB_565; opts.inPurgeable = true; opts.inSampleSize = 4; opts.inInputShareable = true; Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri), null, opts); mImage.setImageBitmap(bitmap); } catch (Exception e) { e.printStackTrace(); } } break; } }通过这种方式就解决了手机拍照OOM的问题。