这篇来学习一下如何调用摄像头拍照。
学习自《第一行代码》
以下内容皆为真机测试。
xml文件
就一个按钮和一个显示图片的。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btnTakePhoto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Photo"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private int takePhoto = 1;
private Uri imageUri;
private File outputImage;
private Button btnTakePhoto;
private ImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnTakePhoto = findViewById(R.id.btnTakePhoto);
imageView = findViewById(R.id.imageView);
// 拍照按钮
btnTakePhoto.setOnClickListener(view -> {
// 创建File对象,用于存储拍照后的图片,命名为output_image.jdp
// 存放在手机SD卡的应用关联缓存目录下
outputImage = new File(getExternalCacheDir(), "output_image.jpg");
if (outputImage.exists()) {
outputImage.delete();
}
try {
outputImage.createNewFile();
// 如果运行设备的系统高于Android 7.0
// 就调用FileProvider的getUriForFile()方法将File对象转换成一个封装过的Uri对象。
// 该方法接收3个参数:Context对象, 任意唯一的字符串, 创建的File对象。
// 这样做的原因:Android 7.0 开始,直接使用本地真实路径的Uri是被认为是不安全的,会抛出FileUriExposedException异常;
// 而FileProvider是一种特殊的ContentProvider,他使用了和ContentProvider类似的机制对数据进行保护,可以选择性地将封装过的Uri共享给外部。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri = FileProvider.getUriForFile(this, "com.example.permissiontest.fileprovider", outputImage);
} else {
// 否则,就调用Uri的fromFile()方法将File对象转换成Uri对象
imageUri = Uri.fromFile(outputImage);
}
// 启动相机
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
// 指定图片的输出地址,这样拍下的照片会被输出到output_image.jpg中。
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, takePhoto);
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == takePhoto) {
if (resultCode == Activity.RESULT_OK) {
// 将拍摄的照片显示出来
try {
// decodeStream()可以将output_image.jpg解析成Bitmap对象。
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
imageView.setImageBitmap(rotateIfRequired(bitmap));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
/**
* 为了防止横屏照片时,返回产生的旋转,所以这里做的是将照片始终正常的竖屏显示
* @param bitmap
* @return
*/
private Bitmap rotateIfRequired(Bitmap bitmap) {
try {
ExifInterface exifInterface = new ExifInterface(outputImage.getPath());
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) return rotateBitmap(bitmap, 90);
else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) return rotateBitmap(bitmap, 180);
else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) return rotateBitmap(bitmap, 270);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Bitmap rotateBitmap(Bitmap bitmap, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate((float)degree);
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
// 将不再需要的Bitmap对象回收
bitmap.recycle();
return rotatedBitmap;
}
}
注册ContentProvider
上述方法中用到了ContentProvider,所以需要去AndroidManifest.xml对它进行注册:
<application
...
<provider
android:authorities="com.example.permissiontest.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
<activity
...
</activity>
</application>
其中android:name属性的值是固定的,android:authorities属性的值需要和刚才FileProvider.getUriForFile()方法的第二个参数相同。
这里还使用了<meta-data>
标签指定Uri的共享路径,并引用了@xml/file_paths资源。现在需要去创建这个资源。
在res目录下新建文件夹xml,然后新建一个File,命名为file_paths.xml,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/"/>
</paths>
其中, external-path是用来指定Uri共享路径的,name属性的值随便填,path值表示共享的具体路径。单斜杠表示将整个SD卡进行共享。
大功告成,现在去真机上测试时没有问题。