Android development skills-the Camera feature of Camera
This article summarizes how to use Camera to take photos of development projects. Camera2 is introduced in api level 21 (5.0.1), while Camera to 6.0 is still usable. Therefore, camera2 is not considered yet.
Camera in the document
Compared with the vast majority of other classes, this document provides a detailed description of Camera, including the steps required during use. Of course, this also shows that it is cumbersome to use.
First, you mustAndroidManifest.xml
Declare the following permissions and features:
Then, take the photo in the following ten steps:
1. Passopen(int)
Method to obtain an instance
2. PassgetParameters()
Method to get the default settings
3. If necessary, modifyCamera.Parameters
Object and callsetParameters(Camera.Parameters)
Set
4. CallsetDisplayOrientation(int)
Set the display direction
5.This step is important, ThroughsetPreviewDisplay(SurfaceHolder)
InputInitializedOfSurfaceHolder
Otherwise, you cannot preview it.
6.This step is also important, ThroughstartPreview()
Start to update your preview page. It must start before you take a picture.
7. CalltakePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)
Take a photo and wait for its callback
8. After taking the photo, the preview will stop. If you want to continue taking the photo, you need to callstartPreview()
.
9. CallstopPreview()
Stop preview.
10.Very important, Callrelease()
Release Camera so that other applications can also use the Camera. Your application should be inonPause()
Released when called.onResume()
And thenopen()
.
The preceding section describes how to use Camera to take photos. Next, let's talk about my application scenarios.
My application scenarios
This is the interface requirement of the project. The following is a circle photo button, followed by a Cancel button. The preview interface (SurfaceView) is added with a view box. The above is a piece of black. Click to take a photo. After taking the photo, you will jump to a cropping image page, so there will be no scenarios where you can take multiple photos consecutively.
The layout file is relatively simple. The following describes how to use Camera in Java code.
Actual use and fill in SurfaceHolder callback
Implementation in ActivitySurfaceHolder.Callback
Interface. ThenonCreate(Bundle)
Method, addSurfaceHolder
.
SurfaceHolder holder = mSurfaceView.getHolder(); holder.addCallback(this);
There are three callback methods, namely, the callback when the surface is created.surfaceCreated(SurfaceHolder)
Is called back when the surface is destroyed.surfaceDestroyed(SurfaceHolder)
And the callback when the surface changessurfaceChanged(SurfaceHolder holder, int, int, int)
. Here we only focus on the callback during creation and destruction, and define a variable to indicate its status.
private boolean mIsSurfaceReady; @Override public void surfaceCreated(SurfaceHolder holder) { mIsSurfaceReady = true; startPreview(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { mIsSurfaceReady = false; }
The startPreview () method is described below.
Turn on the camera
Then open the camera. These codes are inopenCamera
Method.
If (mCamera = null) {try {mCamera = Camera. open ();} catch (RuntimeException e) {if ("Fail to connect to camera service ". equals (e. getMessage () {// The system prompts that the Camera cannot be opened. Please check whether the permission is enabled} else if ("Camera initialization failed ". equals (e. getMessage () {// The system prompts that the camera initialization failed and cannot be enabled} else {// The system prompts that the camera has an unknown error and cannot be opened} finish (); return ;}}
If the camera fails to be opened, we cannot proceed to the next step. Therefore, we will directly turn off the interface after the prompt.
Photo Parameters
final Camera.Parameters cameraParams = mCamera.getParameters(); cameraParams.setPictureFormat(ImageFormat.JPEG); cameraParams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
Set the image format and focus mode respectively. Then, because I am taking a portrait, I still need to rotate Camera 90 degrees.
cameraParams.setRotation(90);
Note:: There are two methods involved in rotation. One is rotating the camera, and the other is rotating the preview. The rotation of the camera is set here.
Continue to note: Due to the compatibility of models, after rotation is set here, the photos taken from some mobile phone photos are vertical, but the photos taken from some mobile phones (such as Samsung) are still horizontal, however, they have related angle attributes in the Exif information of the photo. Therefore, the photos are still horizontal, so we can continue to process them during cropping. As for how to rotate photos, I will discuss it later in my blog.
Dimension parameters
This is still the Camera parameter settings, but I extracted it separately because it is not as simple and straightforward as the preceding parameters, and needs to be calculated. The following are the issues that need attention:
First, there are two kinds of camera width/height ratio: And. Second, we need the ratio of SurfaceView to the ratio of Camera preview size to avoid deformation of the preview result. Due to the resolution problem of the model, our SurfaceView is not full screen (even if the screen is full, we need to consider some virtual Navigation bars and various Amazing resolution models ), we don't need to use the ratio of. We will make the size ratio of Camera preview the same as that of SurfaceView. Note that for some mobile phones, if the preview size is too different from the image size (but the width and height ratio are the same), the photos may have different ranges. For example, when you take a picture, it is clearly a painting that includes a frame, but only the content in the frame is saved.
The following code is written in our openCamera () method. Because we need to be able to get the SurfaceView size, openCamera () is called as follows:
@Override protected void onResume() { super.onResume(); mSurfaceView.post(new Runnable() { @Override public void run() { openCamera(); } }); }
It ensures that surfaceView is fully drawn when openCamera () is called.
ThenopenCamera()
In subsequent code, obtain the width and height ratio of surfaceView first. Note: For surfaceView, I started to write in the layout that the height occupies the rest of the space.
In this case, the aspect ratio is the minimum percentage we can accept.
// Short edge ratio long edge final float ratio = (float) mSurfaceView. getWidth ()/mSurfaceView. getHeight ();
Then obtain the image size supported by the camera to find the most suitable size.
// Set pictureSize List
PictureSizes = cameraParams. getSupportedPictureSizes (); if (mBestPictureSize = null) {mBestPictureSize = findBestPictureSize (pictureSizes, cameraParams. getPictureSize (), ratio);} cameraParams. setPictureSize (mBestPictureSize. width, mBestPictureSize. height );
findBestPictureSize
The Code is as follows. Note: Because we rotate the Camera, the ratio of surfaceView to surfaceView is width divided by height, while that of Camera. Size is height divided by width.
/*** Find the maximum size of the shorter side ratio longer than the acceptable minimum size ** @ param sizes supported size list * @ param defaultSize default size * @ param minRatio camera the minimum ratio of the shorter side to the longer side of the image * @ return returns the calculated size */private Camera. size findBestPictureSize (List
Sizes, Camera. Size defaultSize, float minRatio) {final int MIN_PIXELS = 320*480; sortSizes (sizes); Iterator
It = sizes. iterator (); while (it. hasNext () {Camera. size size = it. next (); // remove the size if (float) size that does not meet the requirement. height/size. width <= minRatio) {it. remove (); continue;} // remove small size if (size. width * size. height <MIN_PIXELS) {it. remove () ;}// returns an if (! Sizes. isEmpty () {return sizes. get (0);} // No choice. Default Value: return defaultSize ;}
Next, set the preview image size:
// Set previewSize List
PreviewSizes = cameraParams. getSupportedPreviewSizes (); if (mBestPreviewSize = null) {mBestPreviewSize = findBestPreviewSize (previewSizes, cameraParams. getPreviewSize (), mBestPictureSize, ratio);} cameraParams. setPreviewSize (mBestPreviewSize. width, mBestPreviewSize. height );
Calculate the preview Size Based on the image size and SurfaceView ratio.
/*** @ Param sizes * @ param defaultSize * @ param pictureSize the image size * @ param minRatio preview the minimum ratio of the shorter side to the longer side * @ return */private Camera. size findBestPreviewSize (List
Sizes, Camera. size defaultSize, Camera. size pictureSize, float minRatio) {final int pictureWidth = pictureSize. width; final int pictureHeight = pictureSize. height; boolean isBestSize = (pictureHeight/(float) pictureWidth)> minRatio; sortSizes (sizes); Iterator
It = sizes. iterator (); while (it. hasNext () {Camera. size size = it. next (); if (float) size. height/size. width <= minRatio) {it. remove (); continue;} // find the same proportion and return if (isBestSize & size. width * pictureHeight = size. height * pictureWidth) {return size ;}// if (! Sizes. isEmpty () {return sizes. get (0);} // No choice. Default Value: return defaultSize ;}
The two abovefindBestxxx
Method, which can be adjusted according to business needs. The overall idea is to sort the dimensions first, and then traverse and exclude the dimensions that do not meet the conditions. If the ratios are the same, the system returns the results directly. If the traversal is not found, the maximum size is returned. If it is excluded, only the default one is returned.
Then, we need to re-set the surfaceView Size Based on the previewSize so that the proportions of the surfaceView are identical, so that the preview will not be distorted.
ViewGroup.LayoutParams params = mSurfaceView.getLayoutParams(); params.height = mSurfaceView.getWidth() * mBestPreviewSize.width / mBestPreviewSize.height; mSurfaceView.setLayoutParams(params);
The next step is to set the parameters:
mCamera.setParameters(cameraParams);
Then preview.
Preview
Because it takes some time for the camera to open, and the surfaceHolder callback also takes some time. I hope that when the camera preparation is complete, you can call back and the surface is also created, you can preview it immediately (try to reduce the time that may be hacked after entering the interface ), so here my code is as follows:
if (mIsSurfaceReady) { startPreview(); }
At the same time, when the surface is created, thisstartPreview()
Method.
startPreview()
The Code is as follows. After camera initialization, first set the SurfaceHolder object, rotate the preview 90 degrees, and then start previewing.
private void startPreview() { if (mCamera == null) { return; } try { mCamera.setPreviewDisplay(mSurfaceView.getHolder()); mCamera.setDisplayOrientation(90); mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); BugReport.report(e); } }
Auto Focus
I hope to enable auto focus when I click preview. Because I put a View in the context menu above surfaceview on the interface, I directly set a click event for this View to trigger auto focus.
The autofocus code is as follows:
/*** Request auto focus */private void requestFocus () {if (mCamera = null | mWaitForTakePhoto) {return;} mCamera. autoFocus (null );}
Here, I only need the camera to focus, not to take a photo after the focus is successful, So I passed a null callback.
The reason for this is that I used to take a photo after focusing successfully, but there are two problems: first, there is a process of focusing, so that the photo will be slow after focusing, second, it is possible that the preview interface is exactly what we want when we take a picture, but a pair of focal points may fail to focus, resulting in no photos or Blurred photos.
Photograph
Taking a photo is also an asynchronous callback that takes some time, so here I definemWaitForTakePhoto
Variable, indicating that the photo is being taken. You are not allowed to re-focus or re-take a photo during the photo taking process.
private void takePhoto() { if (mCamera == null || mWaitForTakePhoto) { return; } mWaitForTakePhoto = true; mCamera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { onTakePhoto(data); mWaitForTakePhoto = false; } }); }
Save the photo. The data returned here can be directly written into the file, which is a jpg image.
Private void onTakePhoto (byte [] data) {final String tempPath = mOutput + "_"; FileOutputStream fos = null; try {fos = new FileOutputStream (tempPath); fos. write (data); fos. flush (); // start my cropping interface} catch (Exception e) {BugReport. report (e);} finally {IOUtils. close (fos );}}
Camera opening and closing and Activity Lifecycle
@Override protected void onResume() { super.onResume(); mSurfaceView.post(new Runnable() { @Override public void run() { openCamera(); } }); } @Override protected void onPause() { super.onPause(); closeCamera(); }
When you turn off the camera, you must first cancel auto focus. Otherwise, if you turn off the camera and focus on auto focus, an exception will occur. Stop preview and then release it:
private void closeCamera() { if (mCamera == null) { return; } mCamera.cancelAutoFocus(); stopPreview(); mCamera.release(); mCamera = null; }
Summary
1, all the code of this class can see: https://gist.github.com/msdx/f8ca0fabf0092f67d829. No Demo project, no Demo project, no Demo project.
2. The document is very important.
3. I don't guarantee that my code is okay, at least I haven't found it now. If you have any questions, please submit them.
4. Pay attention to camera opening and releasing.
5. Pay attention to the camera rotation settings of different models. Especially Samsung.
6. The size calculation and previewSize ratio must be the same as the ratio shown by surfaceView.