Camera Use - Implement Simple Custom Cameras

Posted by hyster on Sat, 25 May 2019 23:20:57 +0200

This article summarizes the implementation of custom cameras using the Camera API

Previous articles Detailed description of Android Photo Function This paper introduces the camera application of the call system to complete the photography function.

But many times, applications need to customize the camera photo function. After Android 5.0, Camera 2.0 was introduced to replace the previous Camera API, but many times we still need to use the Camera API to customize the camera.

Key Knowledge Points

  1. The use of SurfaceView, the camera preview interface is achieved through SurfaceView;
  2. The use of Camera API;
  3. Save and read the files generated by taking photos.

Permission application

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera"/>

Camera permissions, file storage permissions.

Camera interface

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/surfaceview"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Photograph"
        android:layout_gravity="center|bottom"
        android:onClick="takePhoto"/>

</FrameLayout>

Why use SurfaceView here, because ordinary View can not achieve efficient rendering of photo page view.

Camera API usage

Camera initialization
 /**
     * Initialization of Camera Objects
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

The Camera object is obtained by the Camera.open() method.

Surface Holder initialization

 @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

Surface Holder is the key method of Surface View. Surface Holder is acquired by Surface View and its callback method is implemented.

Implementation of Surface Holder's Callback Method
 @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //Close first, open again
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }

Start Preview (mCamera, holder) is the method I implemented to preview the content of the camera, and release Camera () is the method to release the camera resources.

Preview Camera Content
 /**
     * Start previewing camera content
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //Adjust Camera Preview Angle
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

By associating Camera with Surface Holder, you can display Camera's preview content in Surface View.

Release Camera Resources
/**
     * Release Camera Resources
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

Get the photo generated

/**
     * Photo Button Click Event
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //The first two parameters can be null, and the third method is photo callback.
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

Here we get the callback byte stream, then generate the file to save, and finally put the file data into Intent, close the current page.
The mPictureCallback method is the method of callback for the result of camera photography.

private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };

The setParameters() method is our settings for some of Camera's parameters:

/**
     * Start previewing camera content
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //Adjust Camera Preview Angle
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

The key methods are basically introduced. Next, I will release all the code of the Camera Activity interface to facilitate your hands-on operation.

public class Camera1Activity extends BaseActivity implements SurfaceHolder.Callback{

    @BindView(R.id.surfaceview)SurfaceView mPreview;

    private File mFile;
    private Camera mCamera;
    private SurfaceHolder mHolder;

    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            try {
                FileOutputStream fos = new FileOutputStream(mFile);
                fos.write(data);
                fos.close();

                Intent intent = new Intent();
                intent.putExtra("file",mFile.getAbsolutePath());
                setResult(0,intent);
                finish();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };


    @Override
    protected int getContentViewResId() {
        return R.layout.camera1;
    }

    @Override
    protected void onResume() {
        super.onResume();

        if(mCamera == null){
            mCamera = initCamera();
            if(mHolder != null){
                startPreview(mCamera,mHolder);
            }
        }

    }

    @Override
    protected void onPause() {
        super.onPause();

        releaseCamera();
    }

    @Override
    protected void preInitData() {

        mFile = new File(getIntent().getStringExtra("file"));

        mHolder = mPreview.getHolder();
        mHolder.addCallback(this);
    }

    @OnClick(R.id.surfaceview)
    public void onSurfaceviewClick(){
        mCamera.autoFocus(null);
    }

    /**
     * Photo Button Click Event
     * @param view
     */
    public void takePhoto(View view){
        setParameters();
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(success){
                    //The first two parameters can be null, and the third method is photo callback.
                    mCamera.takePicture(null,null,mPictureCallback);
                }
            }
        });
    }

    /**
     * Initialization of Camera Objects
     * @return
     */
    private Camera initCamera(){

        Camera camera;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            camera = null;
            e.printStackTrace();
        }

        return camera;
    }

    /**
     * Start previewing camera content
     */
    private void startPreview(Camera camera ,SurfaceHolder holder){
        try {
            camera.setPreviewDisplay(holder);
            //Adjust Camera Preview Angle
            camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Release Camera Resources
     */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * Setting Camera Parameters
     */
    private void setParameters(){
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPictureFormat(ImageFormat.JPEG);
        parameters.setPreviewSize(800,400);
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        //Close first, open again
        mCamera.stopPreview();
        startPreview(mCamera,holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }
}

The photo function is realized. Next, we deal with the display operation of the photos after taking pictures. When you read the article carefully, you will find that when we jump into Camera Activity, we pass in the path of the photo file.

 public void photo(View view){
        Intent intent = new Intent(this,Camera1Activity.class);
        intent.putExtra("file",createImageFile("mFile").toString());
        startActivityForResult(intent,TAKE_PHOTO_CUSTOM);
    }

    private File createImageFile(String fileName){
        File dir = new File(getCacheDir(),"images");
        if(!dir.exists())
            dir.mkdirs();
        return new File(dir,fileName);
    }

At the same time, we put the photo path into Intent.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == TAKE_PHOTO_CUSTOM){
            if(data != null){
//                Bitmap resultBitmap = BitmapFactory.decodeFile(filePath);
//                mImageView.setImageBitmap(resultBitmap);
                try {
                    String filePath = data.getStringExtra("file");
                    FileInputStream fis = new FileInputStream(filePath);
                    Bitmap bitmap = BitmapFactory.decodeStream(fis);
                    Matrix matrix = new Matrix();
                    matrix.setRotate(90);
                    bitmap = Bitmap.createBitmap(bitmap,0,0,
                            bitmap.getWidth(),bitmap.getHeight(),matrix,true);
                    mImageView.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

Because if I take the photo out directly, the effect of the photo display is rotated 90 degrees. As for why, careful students may find that we rotated Camera 90 degrees when using the Camera API, because Camera defaults to a horizontal screen display. So I rotated the image view displayed by the photo 90 degrees in OnActivity Result so that the photo could be displayed correctly in the Image View.

Camera's use of brief answers is introduced here to achieve a simple photo function. As for the application of customized photography function in the project, I believe that on this basis, some improvement can meet the general functional requirements.

Feeling helpful, students are welcome to leave messages and exchange, point praise and attention! Thank you.

Topics: Android SurfaceView xml encoding