Android调用摄像头拍照

这篇来学习一下如何调用摄像头拍照。

学习自《第一行代码》

以下内容皆为真机测试。

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()方法的第二个参数相同。

这里还使用了标签指定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卡进行共享。

大功告成,现在去真机上测试时没有问题。

你可能感兴趣的:(Android,java,android,java)