Android Custom Camera to Realize Photographing Function

  • 2021-09-11 21:27:22
  • OfStack

This paper records the simple photo-taking function realized by custom Camera.

The Camera class is not recommended after 5.0. Instead, the class under the android. hardware. camera2 package is used in this article, Camera.
We first customize an View to inherit SurfaceView:


public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback {
 private SurfaceHolder mHolder;
 private Camera mCamera;
 private static final int ORIENTATION = 90;
 private int mScreenWidth;
 private int mScreenHeight;
 private boolean isOpen;

 public CameraSurfaceView(Context context, AttributeSet attrs) {
  super(context, attrs);
  getScreenMatrix(context);
  mHolder = getHolder();
  mHolder.addCallback(this);
  mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
 }

 private void getScreenMatrix(Context context) {
  WindowManager WM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  DisplayMetrics outMetrics = new DisplayMetrics();
  WM.getDefaultDisplay().getMetrics(outMetrics);
  mScreenWidth = outMetrics.widthPixels;
  mScreenHeight = outMetrics.heightPixels;
 }

 public void takePicture(Camera.ShutterCallback mShutterCallback, Camera.PictureCallback rawPictureCallback, Camera.PictureCallback jpegPictureCallback) {
  if (mCamera != null)
   mCamera.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
 }

 public void startPreview() {
  mCamera.startPreview();
 }

 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  if (!checkCameraHardware(getContext()))
   return;
  if (mCamera == null) {
   isOpen = safeCameraOpen(Camera.CameraInfo.CAMERA_FACING_BACK);
  }
  if (!isOpen) {
   return;
  }
  mCamera.setDisplayOrientation(ORIENTATION);
  try {
   mCamera.setPreviewDisplay(holder);

  } catch (IOException e) {
   e.printStackTrace();
  }
 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  if (mCamera != null) {
   setCameraParams(mScreenWidth, mScreenHeight);
   mCamera.startPreview();
  }
 }

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

 private boolean safeCameraOpen(int id) {
  boolean qOpened = false;
  try {
   releaseCameraAndPreview();
   mCamera = Camera.open(id);
   qOpened = (mCamera != null);
  } catch (Exception e) {
   e.printStackTrace();
  }

  return qOpened;
 }

 private void releaseCameraAndPreview() {
  if (mCamera != null) {
   mCamera.stopPreview();
   mCamera.release();
   mCamera = null;
  }
 }

 private boolean checkCameraHardware(Context context) {
  if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
   return true;
  } else {
   return false;
  }
 }


 @Override
 public void onAutoFocus(boolean success, Camera camera) {

 }

 private void setCameraParams(int width, int height) {
  Camera.Parameters parameters = mCamera.getParameters();
  //  Object supported by the camera PictureSize List 
  List<Camera.Size> pictureSizeList = parameters.getSupportedPictureSizes();
  /** Select the appropriate resolution from the list */
  Camera.Size picSize = getProperSize(pictureSizeList, ((float) height / width));
  if (null == picSize) {
   picSize = parameters.getPictureSize();
  }
  //  According to the selected PictureSize Reset SurfaceView Size 
  float w = picSize.width;
  float h = picSize.height;
  parameters.setPictureSize(picSize.width, picSize.height);
  this.setLayoutParams(new RelativeLayout.LayoutParams((int) (height * (h / w)), height));
  //  Object supported by the camera PreviewSize List 
  List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
  Camera.Size preSize = getProperSize(previewSizeList, ((float) height) / width);
  if (null != preSize) {
   parameters.setPreviewSize(preSize.width, preSize.height);
  }

  parameters.setJpegQuality(100); //  Setting photo quality 
  if (parameters.getSupportedFocusModes().contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
   parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//  Continuous focusing mode 
  }

  mCamera.setDisplayOrientation(90);//  Settings PreviewDisplay The effect is how many degrees to rotate the captured picture 
  mCamera.setParameters(parameters);

 }

 /**
  *  Choose the right resolution 
  */
 private Camera.Size getProperSize(List<Camera.Size> pictureSizeList, float screenRatio) {
  Camera.Size result = null;
  for (Camera.Size size : pictureSizeList) {
   float currentRatio = ((float) size.width) / size.height;
   if (currentRatio - screenRatio == 0) {
    result = size;
    break;
   }
  }

  if (null == result) {
   for (Camera.Size size : pictureSizeList) {
    float curRatio = ((float) size.width) / size.height;
    if (curRatio == 4f / 3) {//  Default w:h = 4:3
     result = size;
     break;
    }
   }
  }

  return result;
 }
}

The code is not difficult, when View is created, complete the initialization of Camera, then set the parameters of Camera (picture size, quality, etc.), and finally don't forget to release the resources when View is destroyed.

After the control is defined, we will use it and add OK to the layout file:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 >

 <com.padoon.cameratest.CameraSurfaceView
  android:id="@+id/sv_camera"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_weight="4"/>

 <ImageView
  android:id="@+id/img_take_photo"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_alignParentBottom="true"
  android:layout_alignParentRight="true"
  android:layout_gravity="bottom"
  android:layout_marginBottom="10dp"
  android:layout_marginRight="10dp"
  android:src="@mipmap/icon_camera"/>
</RelativeLayout>

Then complete the photo function in Activity:


public class CameraActivity extends AppCompatActivity {
 private boolean isClick = true;
 private static final String PATH_IMAGES = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "easy_check";
 private CameraSurfaceView mCameraSurfaceView;
 // Callback of photo shutter 
 private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
  @Override
  public void onShutter() {

  }
 };
 // Callback to return the original data after taking pictures 
 private Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
  @Override
  public void onPictureTaken(byte[] data, Camera camera) {

  }
 };
 // Callback to return compressed data after taking pictures 
 private Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
  @Override
  public void onPictureTaken(byte[] data, Camera camera) {
   mCameraSurfaceView.startPreview();
   saveFile(data);
   Toast.makeText(CameraActivity.this, " Successful photographing ", Toast.LENGTH_SHORT).show();
   isClick = true;

  }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ImageView img_take_photo = (ImageView) findViewById(R.id.img_take_photo);
  mCameraSurfaceView = (CameraSurfaceView) findViewById(R.id.sv_camera);
  img_take_photo.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    takePhoto();
   }
  });
 }

 public void takePhoto() {
  if (isClick) {
   isClick = false;
   mCameraSurfaceView.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
  }
 }
 // Save pictures to hard disk 
 public void saveFile(byte[] data) {
  String fileName = UUID.randomUUID().toString() + ".jpg";
  FileOutputStream outputStream = null;
  try {
   File file = new File(PATH_IMAGES);
   if (!file.exists()) {
    file.mkdirs();
   }
   outputStream = new FileOutputStream(PATH_IMAGES + File.separator + fileName);
   BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
   bufferedOutputStream.write(data, 0, data.length);
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    outputStream.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }
}

Finally, remember to add photo and disk operation permissions:


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

By this very simple photo Demo, it can only be used as Demo, and there is still a long way to go before it is officially used. Please record it again.

Download: Source code


Related articles: