手机获取图片两种方式
- 通过相机拍照
- 从系统图库中选取
基本流程
无论是选择拍照还是选择现成的图片进行裁剪,都要分为以下三个步骤:
S1. 调用系统的拍照界面或者图库的管理界面,由于我们要对该界面的操作结果进行监听,因此我们在启动Activity的时候使用startActivityForResult(Intent intent, int requestCode)
函数。假设我们在界面A1
中用startActivityForResult
的方式启动界面A2
,那么当A2
关闭之后,会自动调用A1
的onActivityResult
函数(因此我们需要重写该函数)
S2. 在onActivityResult(int requestCode, int resultCode, Intent data)
函数中,data中包含被关闭的界面(A2
)传递过来的数据,所以我们需要的”被选择的图片“或者”拍照生成的图片“信息就包含在这个里面,此时我们就可以在这个操作的基础之上接着进行图片的裁剪操作,同样是调用系统的界面,同样是使用startActivityForResult
的方式
S3. 最后还是一样,在onActivityResult
中我们可以得到图片裁剪界面回传过来的intent的对象,里面就包含了裁剪过的图片的数据,我们获取数据,接下来的操作就自定义了
还好系统给我们提供了很多现成的操作界面,所以功能实现起来并不太复杂
准备工作
在使用startActivityForResult
需要传入一个名为requestCode
的参数,在onActivityResult
函数中我们还会获得一个名为resultCode
的参数,这个具体可以这么来解释:
对于一个界面A1
,我们可以通过startActivityForResult
的方式来启动很多个界面A2
、A3
…,那么在onActivityResult
函数中就会有一个疑问了:这个函数到底是因为我们启动的A2
、A3
…哪个界面被关闭而调用的呢?
所以这时,就是requestCode
发挥作用的时候了,通过它来区分不同的启动界面,让我们知道到底是A2
、A3
…中的哪个界面关闭后传递数据过来,这样我们就不会产生歧义了。
而resultCode
顾名思义就能知道是界面的执行结果代码,就比如在选择图片时,正常的流程当然是用户点击了一个图片然后返回,可是如果用户按下了键盘上的Back键,这时候我们怎么能做出正确的相应操作?只有通过这个参数,它可能的取值有Activity.RESULT_OK
、Activity.RESULT_CANCELED
等等,这样就能帮助我们做出正确的判断了。
所以就我们第一步就是先要区分一下拍照界面和图库选择界面,我们在类中定义:
final int REQUEST_CAPTURE = 1; //表示拍照界面
final int REQUEST_SELECT = 2; //表示图库选择界面
final int REQUEST_CROP = 3; //表示图片裁剪界面
由拍照获取图片
进入拍照界面很简单,代码如下:
Intent intent = new Intent();
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, REQUEST_CAPTURE);
从图库选择图片
Intent intent = new Intent();
//"image/*"表示通配所有图片类型的文件,包含"image/png"、"image/jpeg"等
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(intent, REQUEST_SELECT);
上述代码中为什么不是
intent.setAction(Intent.ACTION_PICK)
?
图片裁剪
这一步是建立在前面的图片获取基础之上,我们需要重写Activity
中的onActivityResult
函数,代码如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//操作失败,用户没有拍照成功或者没有选择图片时
if (Activity.RESULT_OK != resultCode) {
return ;
}
switch (requestCode) {
//拍照
case REQUEST_CAPTURE:
//从图库中选择
case REQUEST_SELECT: {
//启动裁剪界面
Intent intent = new Intent("com.android.camera.action.CROP");
intent.putExtra("crop", "true");
intent.putExtra("scale", true);
intent.putExtra("noFaceDetection", true);
//裁剪图片的长宽比
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//裁剪图片的输出大小
intent.putExtra("outputX", 512);
intent.putExtra("outputY", 512);
//这里我们不把图片以Bitmap形式进行返回,因为数据量过大
//我们将裁剪后生成的图片保存到本地
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("裁剪输出文件路径"));
//设置需要裁剪的图片数据
intent.setDataAndType(data.getData(), "image/*");
startActivityForResult(intent, REQUEST_CROP);
} break;
//裁剪图片结果
case REQUEST_CROP: {
// TODO what you wanna do...
// 裁剪生成的图片在上述代码中的"裁剪输出文件路径"位置
} break;
default:
break;
}
}
可能出现的问题
到这里似乎看起来就大功告成了,然而部分手机的运行结果可能事与愿违,如小米4,在执行拍照返回之后进行裁剪,发现漆黑一片,没有图片,究其原因,是在上述代码中null == data.getData()
导致的。
解决办法如下:
//在启动拍照界面之前添加
//就相当于我们要把拍照生成的图片保存到一个我们自定义的路径中
//在要进行裁剪操作,我们再把这个自定义的路径以Uri的形式传递过去以便找到
File img = new File("拍照图片临时保存路径");
if (img.exists()) {
img.delete();
}
try {
img.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(img));
同时将上述onActivityResult
函数中的:
intent.setDataAndType(data.getData(), "image/*");
修改为:
intent.setDataAndType(Uri.fromFile(new File("拍照图片临时保存路径"), "image/*");
用这种方式的好处就相当于我们不用在信息交换中携带大量的原始数据,因为这样造成的系统开销太大,作为替代,我们只使用一个指向该数据的地址(Android中使用的是
Uri
,原理一样),这样操作起来也就更加有效率(相当于编程中的传值与传地址的区别)。PS:大图裁剪同样用的是上述方法,都把生成的数据先保存到本地之后再通过
Uri
来传递