上次封装的那个我觉得不够完美。再来个2吧
效果图

自定义View
package com.element.wisdomclassbrand.ui.view;
import android.content.Context;
import android.hardware.Camera;
import android.graphics.Point;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.element.common.util.SystemPropertiesUtil;
import com.element.facerecognition.camera.CameraHelper;
import com.element.facerecognition.camera.CameraListener;
import com.element.wisdomclassbrand.R;
import com.element.wisdomclassbrand.app.SessionContext;
import com.element.wisdomclassbrand.databinding.WidgetCameraPhotographBinding;
import com.element.wisdomclassbrand.ui.base.BaseViewBindingConstraintLayout;
import com.eric.jinglecat.app.AppContext;
import org.jetbrains.annotations.Nullable;
public class CameraPhotographWidget extends BaseViewBindingConstraintLayout<WidgetCameraPhotographBinding> implements ViewTreeObserver.OnGlobalLayoutListener{
private CameraHelper cameraHelper;
private WindowManager windowManager;
public CameraPhotographWidget(@Nullable Context context) {
super(context);
}
public CameraPhotographWidget(@Nullable Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CameraPhotographWidget(@Nullable Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected int initLayoutId() {
return R.layout.widget_camera_photograph;
}
@Override
protected void initParams() {
super.initParams();
windowManager = (WindowManager) AppContext.mMainContext.getSystemService(Context.WINDOW_SERVICE);
}
@Override
protected void initListeners() {
super.initListeners();
getBinding().cameraPreview.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getBinding().cameraPreview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
getBinding().cameraPreview.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
initCamera();
}
private void initCamera() {
int cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
if (SessionContext.getDeviceModelType() != SystemPropertiesUtil.UNKNOWN_TYPE) {
cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
cameraHelper = new CameraHelper.Builder()
.previewViewSize(new Point(getBinding().cameraPreview.getMeasuredWidth(), getBinding().cameraPreview.getMeasuredHeight()))
.pictureSizes(new Point(getBinding().cameraPreview.getMeasuredWidth(), getBinding().cameraPreview.getMeasuredHeight()))
.rotation(windowManager.getDefaultDisplay().getRefreshRate() == 0 ? (int) windowManager.getDefaultDisplay().getRefreshRate() : Surface.ROTATION_0)
.specificCameraId(cameraId)
.isMirror(false)
.previewOn(getBinding().cameraPreview)
.build();
cameraHelper.init();
cameraHelper.start();
}
public void takePicture(Camera.ShutterCallback mShutterCallback, Camera.PictureCallback rawPictureCallback, Camera.PictureCallback jpegPictureCallback) {
if (cameraHelper != null) {
cameraHelper.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
}
}
}
视图

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextureView
android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
CameraHelper,该类是核心。
package com.element.facerecognition.camera;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import com.eric.jinglecat.util.LogUtil;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class CameraHelper implements Camera.PreviewCallback {
private static final String TAG = CameraHelper.class.getName();
private static final int NO_CAMERA_ID = -1;
private Camera mCamera;
private int mCameraId;
private Point previewViewSize;
private Point pictureSizes;
private View previewDisplayView;
private Camera.Size previewSize;
private Camera.Size imageSize;
private Point specificPreviewSize;
private int displayOrientation = 0;
private int additionalRotation;
private int rotation;
private boolean isMirror = false;
private Integer specificCameraId;
private CameraListener cameraListener;
private CameraHelper(Builder builder) {
previewDisplayView = builder.previewDisplayView;
specificCameraId = builder.specificCameraId;
cameraListener = builder.cameraListener;
rotation = builder.rotation;
additionalRotation = builder.additionalRotation;
previewViewSize = builder.previewViewSize;
pictureSizes = builder.pictureSizes;
specificPreviewSize = builder.previewSize;
if (builder.previewDisplayView instanceof TextureView) {
isMirror = builder.isMirror;
} else if (isMirror) {
throw new RuntimeException("mirror is effective only when the preview is on a textureView");
}
}
public void init() {
if (previewDisplayView instanceof TextureView) {
((TextureView) this.previewDisplayView).setSurfaceTextureListener(textureListener);
} else if (previewDisplayView instanceof SurfaceView) {
((SurfaceView) previewDisplayView).getHolder().addCallback(surfaceCallback);
}
if (isMirror) {
previewDisplayView.setScaleX(-1);
}
}
private int findCameraIdByFacing(int cameraFacing) {
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
int cameraCount = Camera.getNumberOfCameras();
for (int cameraId = 0; cameraId < cameraCount; cameraId++) {
try {
Camera.getCameraInfo(cameraId, cameraInfo);
if (cameraInfo.facing == cameraFacing) {
return cameraId;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return NO_CAMERA_ID;
}
/**
* 获取当前使用的摄像头ID
* @return 当前摄像头ID
*/
public int getUseCameraId() {
return mCameraId;
}
/**
* 是否后缀摄像头
* @return 是否后缀
*/
public boolean isBackCamera() {
return getUseCameraId() == Camera.CameraInfo.CAMERA_FACING_BACK;
}
private int findCameraId() {
if (mCamera != null || Camera.getNumberOfCameras() == 0) {
return NO_CAMERA_ID;
}
int ultimateCameraId = findCameraIdByFacing(mCameraId);
if (ultimateCameraId != NO_CAMERA_ID) {
return ultimateCameraId;
}
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
ultimateCameraId = findCameraIdByFacing(Camera.CameraInfo.CAMERA_FACING_FRONT);
} else if (mCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
ultimateCameraId = findCameraIdByFacing(Camera.CameraInfo.CAMERA_FACING_BACK);
}
return ultimateCameraId;
}
public void start() {
synchronized (this) {
//若指定了相机ID,则打开指定的相机
if (specificCameraId != null) {
mCameraId = specificCameraId;
} else {//未指定则默认打开后置
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
//查找相机ID
mCameraId = findCameraId();
//没有相机
if (mCameraId == -1) {
if (cameraListener != null) {
cameraListener.onCameraError(new Exception("camera not found"));
}
return;
}
new CameraOpenTask(this).execute(mCameraId);
}
}
private static final class CameraOpenTask extends AsyncTask<Integer, Void, Camera> {
private final WeakReference<CameraHelper> cameraHelperWeakReference;
public CameraOpenTask(CameraHelper cameraHelper) {
cameraHelperWeakReference = new WeakReference<>(cameraHelper);
}
@Override
protected Camera doInBackground(Integer... params) {
return cameraHelperWeakReference.get().safeOpenCamera(params[0]);
}
@Override
protected void onPostExecute(Camera camera) {
CameraHelper mHelper = cameraHelperWeakReference.get();
//验证视图对象是否消失,如果消失则释放Camera
if (mHelper == null || mHelper.previewDisplayView == null || camera == null) {
mHelper.release();
return;
}
mHelper.setCameraParameters(camera);
}
}
private Camera safeOpenCamera(int cameraId) {
synchronized (this) {
Camera camera = null;
try {
//打开前先判断Camera对象是否为空,如果不为空则先释放Camera,然后再打开
releaseCamera();
//打开前先判断视图是否消失,未消失则打开Camera
if (previewDisplayView != null) {
camera = Camera.open(cameraId);
}
} catch (Exception ex) {
ex.printStackTrace();
camera = null;
}
return camera;
}
}
private void setCameraParameters(Camera camera) {
synchronized (this) {
try {
mCamera = camera;
displayOrientation = getCameraOri(rotation);
mCamera.setDisplayOrientation(displayOrientation);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewFormat(ImageFormat.NV21);
//预览大小设置
previewSize = parameters.getPreviewSize();
LogUtil.d(TAG, "before preview width:" + previewSize.width + " height:" + previewSize.height);
List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
if (supportedPreviewSizes != null && supportedPreviewSizes.size() > 0) {
previewSize = getBestSupportedSize(supportedPreviewSizes, previewViewSize);
}
LogUtil.d(TAG, "after preview width:" + previewSize.width + " height:" + previewSize.height);
parameters.setPreviewSize(previewSize.width, previewSize.height); // 预览大小
// 照片大小赋值
imageSize = parameters.getPictureSize();
LogUtil.d(TAG, "getPictureSize width:" + imageSize.width + " height:" + imageSize.height);
List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
if (supportedPictureSizes != null && supportedPictureSizes.size() > 0) {
imageSize = getBestSupportedSize(supportedPictureSizes, pictureSizes);
}
LogUtil.d(TAG, "previewSize width:" + imageSize.width + " height:" + imageSize.height);
parameters.setPictureSize(imageSize.width, imageSize.height); // 照片大小
//对焦模式设置
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
if (supportedFocusModes != null && supportedFocusModes.size() > 0) {
if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
} else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
}
mCamera.setParameters(parameters);
if (previewDisplayView instanceof TextureView) {
mCamera.setPreviewTexture(((TextureView) previewDisplayView).getSurfaceTexture());
} else {
mCamera.setPreviewDisplay(((SurfaceView) previewDisplayView).getHolder());
}
mCamera.setPreviewCallback(this);
mCamera.startPreview();
if (cameraListener != null) {
cameraListener.onCameraOpened(mCamera, mCameraId, displayOrientation, isMirror);
}
} catch (Exception ex) {
ex.printStackTrace();
if (cameraListener != null) {
cameraListener.onCameraError(ex);
}
}
}
}
private int getCameraOri(int rotation) {
int degrees = rotation * 90;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
default:
break;
}
int result;
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraId, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
public void releaseCamera() {
try {
if (mCamera == null) {
return;
}
mCamera.setPreviewCallback(null);
mCamera.setPreviewDisplay(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop() {
synchronized (this) {
releaseCamera();
if (cameraListener != null) {
cameraListener.onCameraClosed();
}
}
}
public boolean isStopped() {
return mCamera == null;
}
public void release() {
stop();
previewDisplayView = null;
specificCameraId = null;
cameraListener = null;
previewViewSize = null;
pictureSizes = null;
specificPreviewSize = null;
previewSize = null;
}
private Camera.Size getBestSupportedSize(List<Camera.Size> sizes, Point previewViewSize) {
if (sizes == null || sizes.size() == 0) {
return mCamera.getParameters().getPreviewSize();
}
Camera.Size[] tempSizes = sizes.toArray(new Camera.Size[0]);
Arrays.sort(tempSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size o1, Camera.Size o2) {
if (o1.width > o2.width) {
return -1;
} else if (o1.width == o2.width) {
if (o1.height > o2.height) {
return -1;
} else if (o1.height < o2.height) {
return 1;
} else {
return 0;
}
} else {
return 1;
}
}
});
sizes = Arrays.asList(tempSizes);
Camera.Size bestSize = sizes.get(0);
float previewViewRatio;
if (previewViewSize != null) {
previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y;
} else {
previewViewRatio = (float) bestSize.width / (float) bestSize.height;
}
if (previewViewRatio > 1) {
previewViewRatio = 1 / previewViewRatio;
}
boolean isNormalRotate = (additionalRotation % 180 == 0);
for (Camera.Size s : sizes) {
LogUtil.d(TAG, "camera size width:" + s.width + " height:" + s.height);
if (specificPreviewSize != null && specificPreviewSize.x == s.width && specificPreviewSize.y == s.height) {
return s;
}
if (isNormalRotate) {
if (Math.abs((s.height / (float) s.width) - previewViewRatio) < Math.abs(bestSize.height / (float) bestSize.width - previewViewRatio)) {
bestSize = s;
}
} else {
if (Math.abs((s.width / (float) s.height) - previewViewRatio) < Math.abs(bestSize.width / (float) bestSize.height - previewViewRatio)) {
bestSize = s;
}
}
}
return bestSize;
}
public List<Camera.Size> getSupportedPreviewSizes() {
if (mCamera == null) {
return null;
}
return mCamera.getParameters().getSupportedPreviewSizes();
}
public List<Camera.Size> getSupportedPictureSizes() {
if (mCamera == null) {
return null;
}
return mCamera.getParameters().getSupportedPictureSizes();
}
@Override
public void onPreviewFrame(byte[] nv21, Camera camera) {
if (cameraListener != null) {
cameraListener.onPreview(nv21, camera);
}
}
private final TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
if (mCamera != null) {
try {
mCamera.setPreviewTexture(surfaceTexture);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
Log.i(TAG, "onSurfaceTextureSizeChanged: " + width + " " + height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
stop();
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
private final SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera != null) {
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stop();
}
};
public void changeDisplayOrientation(int rotation) {
if (mCamera != null) {
this.rotation = rotation;
displayOrientation = getCameraOri(rotation);
mCamera.setDisplayOrientation(displayOrientation);
if (cameraListener != null) {
cameraListener.onCameraConfigurationChanged(mCameraId, displayOrientation);
}
}
}
public static final class Builder {
/**
* 预览显示的view,目前仅支持surfaceView和textureView
*/
private View previewDisplayView;
/**
* 是否镜像显示,只支持textureView
*/
private boolean isMirror;
/**
* 指定的相机ID
*/
private Integer specificCameraId;
/**
* 事件回调
*/
private CameraListener cameraListener;
/**
* 屏幕的长宽,在选择最佳相机比例时用到
*/
private Point previewViewSize;
/**
* 图片大小
*/
private Point pictureSizes;
/**
* 传入getWindowManager().getDefaultDisplay().getRotation()的值即可
*/
private int rotation;
/**
* 指定的预览宽高,若系统支持则会以这个预览宽高进行预览
*/
private Point previewSize;
/**
* 额外的旋转角度(用于适配一些定制设备)
*/
private int additionalRotation;
public Builder() {
}
public Builder previewOn(View val) {
if (val instanceof SurfaceView || val instanceof TextureView) {
previewDisplayView = val;
return this;
} else {
throw new RuntimeException("you must preview on a textureView or a surfaceView");
}
}
public Builder isMirror(boolean val) {
isMirror = val;
return this;
}
public Builder previewSize(Point val) {
previewSize = val;
return this;
}
public Builder previewViewSize(Point val) {
LogUtil.d(TAG, "preview view width:" + val.x + " height:" + val.y);
previewViewSize = val;
return this;
}
public Builder pictureSizes(Point val) {
LogUtil.d(TAG, "pictureSizes width:" + val.x + " height:" + val.y);
pictureSizes = val;
return this;
}
public Builder rotation(int val) {
LogUtil.d(TAG, "rotation:" + val);
rotation = val;
return this;
}
public Builder additionalRotation(int val) {
additionalRotation = val;
return this;
}
public Builder specificCameraId(Integer val) {
specificCameraId = val;
return this;
}
public Builder cameraListener(CameraListener val) {
cameraListener = val;
return this;
}
public CameraHelper build() {
if (previewViewSize == null) {
Log.e(TAG, "previewViewSize is null, now use default previewSize");
}
if (cameraListener == null) {
Log.e(TAG, "cameraListener is null, callback will not be called");
}
if (previewDisplayView == null) {
throw new RuntimeException("you must preview on a textureView or a surfaceView");
}
return new CameraHelper(this);
}
}
public void takePicture(Camera.ShutterCallback mShutterCallback, Camera.PictureCallback rawPictureCallback, Camera.PictureCallback jpegPictureCallback) {
if (mCamera != null)
mCamera.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
}
}
Actlvity调用

/**
* 拍照
*/
private void photographButton() {
getChildBinding().svCamera.takePicture(mShutterCallback, rawPictureCallback, jpegPictureCallback);
}
/**
* 拍照快门的回调
*/
private final Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
}
};
/**
* 拍照完成之后返回原始数据的回调
*/
private final Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
}
};
/**
* 拍照完成之后返回压缩数据的回调
*/
private final Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
CommonToast.INSTANCE.showToast(ReturnSchoolOrEliminateLeaveActivity.this, getResources().getString(R.string.photograph_button_succeed_text));
queueEvent(new Runnable() {
@Override
public void run() {
saveFile(data);
}
},0L);
}
};
到此结束,下次再见。
评论 (0)