您的位置:首页 > 移动开发 > Android开发

android:拍照,相册剪裁的实现方式

2016-07-21 20:34 615 查看
     一,仅拍照

    首先先给大家看效果图:







   点击拍照,拍照完成然后把照片显示在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:
附加选项数据类型描述
cropString发送裁剪信号
aspectXintX方向上的比例
aspectYintY方向上的比例
outputXint裁剪区的宽
outputYint裁剪区的高
scaleboolean是否保留比例
return-databoolean是否将数据保留在Bitmap中返回
dataParcelable相应的Bitmap数据
circleCropString圆形裁剪区域?
MediaStore.EXTRA_OUTPUT ("output")URI将URI指向相应的file:///...,详见代码示例
outputFormatString输出格式,一般设为Bitmap格式:Bitmap.CompressFormat.JPEG.toString()
noFaceDetectionboolean是否取消人脸识别功能
  这里的参数可以选择性的使用;

  在这里我们要使用把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的问题。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: