因为需要在照相页面显示一些效果,所以只能自己实现照相页面,5.0以下可以使用Camera+SurfaceView实现,在这里只考虑了5.0以上使用了Camera2+TextureView+GlSurfaceView实现。使用GlSurfaceView可以实现一些特殊预览显示效果,比如说黑白,美白,底片等。
这里是两种效果的样子:

下面是主要的Camera2 api的调用,这里需要注意的是图片处理,如果使用Intent传数据,就不能直接传bitmap对象,intent对图片大小有限制很容易超出崩溃,如果传递byte[] 也需要注意大小,有的手机像素过高也会导致崩溃,这里只进行了简单的图片压缩,照相的特殊效果使用的是ColorMatrix 的矩阵设置,这个仅仅是更改照相之后的照片效果,之后会介绍如何处理预览显示的效果:


import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Size;
import android.view.Surface;import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;/*** camera2 的方法调用封装*/
public class CameraV2 {private Activity mActivity;private CameraDevice mCameraDevice;private String mCameraId;private Size mPreviewSize;private HandlerThread mCameraThread;private Handler mCameraHandler;private SurfaceTexture mSurfaceTexture;private CaptureRequest.Builder mCaptureRequestBuilder;private CaptureRequest mCaptureRequest;private CameraCaptureSession mCameraCaptureSession;private ImageReader mImageReader;private CaptureCallBack mCallBack;private boolean mIsPortrait=true;//默认为竖直状态public CameraV2(Activity activity) {mActivity = activity;startCameraThread();}/***  初始配置,和拍照之后图片的图像数据回调* @param width 预览相机宽* @param height  预览相机高* @param fileName 图片保存的文件名*/public void setupCamera(int width, int height, final String fileName) {final CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);try {if(cameraManager!=null) {for (String id : cameraManager.getCameraIdList()) {CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {//判断前后摄像头,这里使用后摄像头continue;}StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);mPreviewSize = getCloselyPreSize(width, height, map.getOutputSizes(SurfaceTexture.class));mCameraId = id;}}// 创建一个ImageReader对象,用于获取摄像头的图像数据mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),mPreviewSize.getHeight(),ImageFormat.JPEG, 1);mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {// 当照片数据可用时激发该方法@Overridepublic void onImageAvailable(final ImageReader reader) {// 获取捕获的照片数据Image image = reader.acquireNextImage();ByteBuffer buffer = image.getPlanes()[0].getBuffer();final byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);Handler handler=new Handler(Looper.getMainLooper());handler.post(new Runnable() {@Overridepublic void run() {BitmapFactory.Options options = new BitmapFactory.Options();//只保存图片尺寸大小,不保存图片到内存options.inJustDecodeBounds = false;//缩放比例options.inSampleSize = 2;// 根据拍照所得的数据创建位图Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,bytes.length, options);int width = options.outWidth;Matrix matrix = new Matrix();matrix.setRotate(90);//旋转90度,照出来的图片初始是横着的float rate = (float)bitmap.getHeight() / 1080;//适配设置float marginRight = (float) 292 * rate;float marginTop= (float) 142 * rate;float cropBitmapWidth = (float) 1240 * rate;float cropBitmapHeight = (float) 796 * rate;//创建裁剪之后的bitmap,原图片不能直接操作bitmap = Bitmap.createBitmap(bitmap, (int)(width-marginRight-cropBitmapWidth), (int)(marginTop), (int)(cropBitmapWidth), (int)(cropBitmapHeight), matrix, true);//这里是对照相得到的图片进行处理 黑白效果//Canvas canvas=new Canvas(bitmap);//Paint paint=new Paint();//ColorMatrix colorMatrix=new ColorMatrix(new float[]{//    0.213f, 0.715f,0.072f,0,0,//    0.213f, 0.715f,0.072f,0,0,//    0.213f, 0.715f,0.072f,0,0,//    0,0,0,1,0,//});//paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));//canvas.drawBitmap(bitmap,0,0,paint);//746px // 1198pxmCallBack.photoData(bitmap);}});if(fileName==null){image.close();return;}// 使用IO流将照片写入指定文件File file = new File(fileName);try (FileOutputStream output = new FileOutputStream(file)) {output.write(bytes);} catch (Exception e) {e.printStackTrace();}finally {image.close();}}}, null);//最后一个参数可控制结果执行在哪个线程因为保存图片需在子线程执行,并没有指定线程} catch (Exception e) {e.printStackTrace();}}/*** 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择)** @param surfaceWidth*            需要被进行对比的原宽* @param surfaceHeight*            需要被进行对比的原高* @param preSizeList*            需要对比的预览尺寸列表* @return 得到与原宽高比例最接近的尺寸*/private Size getCloselyPreSize(int surfaceWidth, int surfaceHeight,Size[] preSizeList) {int reqTmpWidth;int reqTmpHeight;// 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高if (mIsPortrait) {reqTmpWidth = surfaceHeight;reqTmpHeight = surfaceWidth;} else {reqTmpWidth = surfaceWidth;reqTmpHeight = surfaceHeight;}//先查找preview中是否存在与surfaceview相同宽高的尺寸for(Size size : preSizeList){if((size.getWidth() == reqTmpWidth) && (size.getHeight() == reqTmpHeight)){return size;}}// 得到与传入的宽高比最接近的sizefloat reqRatio = ((float) reqTmpWidth) / reqTmpHeight;float curRatio, deltaRatio;float deltaRatioMin = Float.MAX_VALUE;Size retSize = null;for (Size size : preSizeList) {curRatio = ((float) size.getWidth()) / size.getHeight();deltaRatio = Math.abs(reqRatio - curRatio);if (deltaRatio < deltaRatioMin) {deltaRatioMin = deltaRatio;retSize = size;}}return retSize;}/*** 创建照相机执行线程*/private void startCameraThread() {mCameraThread = new HandlerThread("CameraThread");mCameraThread.start();mCameraHandler = new Handler(mCameraThread.getLooper());}public boolean openCamera() {CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);try {if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {return false;}if(cameraManager!=null) {cameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);}} catch (CameraAccessException e) {e.printStackTrace();return false;}return true;}private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {mCameraDevice = camera;}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {camera.close();mCameraDevice = null;}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {camera.close();mCameraDevice = null;}};/*** 设置预览显示的surfaceTexture*/public void setPreviewTexture(SurfaceTexture surfaceTexture) {mSurfaceTexture = surfaceTexture;}/*** 预览显示*/public void startPreview() {mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());Surface surface = new Surface(mSurfaceTexture);try {mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);mCaptureRequestBuilder.addTarget(surface);mCameraDevice.createCaptureSession(Arrays.asList(surface,mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {try {mCaptureRequest = mCaptureRequestBuilder.build();mCameraCaptureSession = session;mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {
//                    ToastUtils.showLong("配置失败");}}, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}public void capture(CaptureCallBack callBack) {mCallBack=callBack;try {//首先我们创建请求拍照的CaptureRequestfinal CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);//设置CaptureRequest输出到mImageReadermCaptureBuilder.addTarget(mImageReader.getSurface());//设置拍照方向 默认竖屏
//            mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(ScreenUtils.getScreenRotation(ActivityUtils.getTopActivity())));//这个回调接口用于拍照结束时重启预览,因为拍照会导致预览停止mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {super.onCaptureCompleted(session, request, result);}}, mCameraHandler);CameraCaptureSession.CaptureCallback mImageSavedCallback = new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureCompleted(@NonNull CameraCaptureSession session,@NonNull CaptureRequest request,@NonNull TotalCaptureResult result) {}};//停止预览mCameraCaptureSession.stopRepeating();//开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片mCameraCaptureSession.capture(mCaptureBuilder.build(), mImageSavedCallback, null);} catch (CameraAccessException e) {e.printStackTrace();}}/*** 重新开启预览*/public void restartPreview() {try {//执行setRepeatingRequest方法就行了,注意mCaptureRequest是之前开启预览设置的请求mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);} catch (CameraAccessException e) {e.printStackTrace();}}public interface CaptureCallBack{void photoData(Bitmap bytes);}public void closeCamera(){mCameraDevice.close();mCameraDevice = null;mCameraThread.interrupt();mCameraHandler=null;}
}

自定义的GlSurfaceView,和GlSurfaceView渲染器

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;/*** 显示界面*/public class CameraV2GLSurfaceView extends GLSurfaceView {public CameraV2GLSurfaceView(Context context) {super(context);}public CameraV2GLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);}public void init(CameraV2 camera, boolean isPreviewStarted, Context context) {setEGLContextClientVersion(2);CameraV2Renderer cameraV2Renderer = new CameraV2Renderer();cameraV2Renderer.init(this, camera, isPreviewStarted, context);setRenderer(cameraV2Renderer);}
}
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLSurfaceView;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_FRAMEBUFFER;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glBindFramebuffer;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGenFramebuffers;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniform1i;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.GLES20.glVertexAttribPointer;
import static android.opengl.GLES20.glViewport;/*** 渲染器*/
public class CameraV2Renderer implements GLSurfaceView.Renderer {private Context mContext;private CameraV2GLSurfaceView mCameraV2GLSurfaceView;private CameraV2 mCamera;private boolean bIsPreviewStarted;private int mOESTextureId = -1;private SurfaceTexture mSurfaceTexture;private float[] transformMatrix = new float[16];private FloatBuffer mDataBuffer;private int mShaderProgram = -1;private int[] mFBOIds = new int[1];public void init(CameraV2GLSurfaceView surfaceView, CameraV2 camera, boolean isPreviewStarted, Context context) {mContext = context;mCameraV2GLSurfaceView = surfaceView;mCamera = camera;bIsPreviewStarted = isPreviewStarted;}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {mOESTextureId = RawUtils.createOESTextureObject();FilterEngine filterEngine = new FilterEngine(mContext);mDataBuffer = filterEngine.getBuffer();mShaderProgram = filterEngine.getShaderProgram();glGenFramebuffers(1, mFBOIds, 0);glBindFramebuffer(GL_FRAMEBUFFER, mFBOIds[0]);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {glViewport(0, 0, width, height);}@Overridepublic void onDrawFrame(GL10 gl) {if (mSurfaceTexture != null) {mSurfaceTexture.updateTexImage();mSurfaceTexture.getTransformMatrix(transformMatrix);}if (!bIsPreviewStarted) {bIsPreviewStarted = initSurfaceTexture();bIsPreviewStarted = true;return;}//glClear(GL_COLOR_BUFFER_BIT);glClearColor(1.0f, 0.0f, 0.0f, 0.0f);int aPositionLocation = glGetAttribLocation(mShaderProgram, FilterEngine.POSITION_ATTRIBUTE);int aTextureCoordLocation = glGetAttribLocation(mShaderProgram, FilterEngine.TEXTURE_COORD_ATTRIBUTE);int uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_MATRIX_UNIFORM);int uTextureSamplerLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_SAMPLER_UNIFORM);glActiveTexture(GL_TEXTURE_EXTERNAL_OES);glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);glUniform1i(uTextureSamplerLocation, 0);glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);if (mDataBuffer != null) {mDataBuffer.position(0);glEnableVertexAttribArray(aPositionLocation);glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mDataBuffer);mDataBuffer.position(2);glEnableVertexAttribArray(aTextureCoordLocation);glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mDataBuffer);}//glDrawElements(GL_TRIANGLE_FAN, 6,GL_UNSIGNED_INT, 0);//glDrawArrays(GL_TRIANGLE_FAN, 0 , 6);glDrawArrays(GL_TRIANGLES, 0, 6);//glDrawArrays(GL_TRIANGLES, 3, 3);glBindFramebuffer(GL_FRAMEBUFFER, 0);}/*** 初始化SurfaceTexture*/private boolean initSurfaceTexture() {if (mCamera == null || mCameraV2GLSurfaceView == null) {return false;}mSurfaceTexture = new SurfaceTexture(mOESTextureId);mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {mCameraV2GLSurfaceView.requestRender();}});mCamera.setPreviewTexture(mSurfaceTexture);mCamera.startPreview();return true;}
}
import android.content.Context;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glGetError;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glUseProgram;public class FilterEngine {private FloatBuffer mBuffer;private int mShaderProgram;public FilterEngine( Context context) {mBuffer = createBuffer(vertexData);int vertexShader = loadShader(GL_VERTEX_SHADER, RawUtils.readShaderFromResource(context, R.raw.base_vertex_shader));int fragmentShader = loadShader(GL_FRAGMENT_SHADER, RawUtils.readShaderFromResource(context, R.raw.base_fragment_shader));mShaderProgram = linkProgram(vertexShader, fragmentShader);}private static final float[] vertexData = {1f, 1f, 1f, 1f,-1f, 1f, 0f, 1f,-1f, -1f, 0f, 0f,1f, 1f, 1f, 1f,-1f, -1f, 0f, 0f,1f, -1f, 1f, 0f};public static final String POSITION_ATTRIBUTE = "aPosition";public static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";public static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";public static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";private FloatBuffer createBuffer(float[] vertexData) {FloatBuffer buffer = ByteBuffer.allocateDirect(vertexData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();buffer.put(vertexData, 0, vertexData.length).position(0);return buffer;}private int loadShader(int type, String shaderSource) {int shader = glCreateShader(type);if (shader == 0) {throw new RuntimeException("Create Shader Failed!" + glGetError());}glShaderSource(shader, shaderSource);glCompileShader(shader);return shader;}private int linkProgram(int verShader, int fragShader) {int program = glCreateProgram();if (program == 0) {throw new RuntimeException("Create Program Failed!" + glGetError());}glAttachShader(program, verShader);glAttachShader(program, fragShader);glLinkProgram(program);glUseProgram(program);return program;}public int getShaderProgram() {return mShaderProgram;}public FloatBuffer getBuffer() {return mBuffer;}
}

其中的base_fragment_shader这种资源文件需要在如图所示的地方创建:

内容分别为:
base_fragment_shader(就是在这里处理预览的显示特殊效果):

#extension GL_OES_EGL_image_external : requireprecision mediump float;uniform samplerExternalOES uTextureSampler;varying vec2 vTextureCoord;void main(){vec4 vCameraColor = texture2D(uTextureSampler, vTextureCoord);float fGrayColor = (0.3*vCameraColor.r + 0.59*vCameraColor.g + 0.11*vCameraColor.b);//黑白滤镜gl_FragColor = vec4(vCameraColor.r, vCameraColor.g, vCameraColor.b, 1.0);}

base_vertex_shader:

attribute vec4 aPosition;
uniform mat4 uTextureMatrix;
attribute vec4 aTextureCoordinate;
varying vec2 vTextureCoord;
void main()
{vTextureCoord = (uTextureMatrix * aTextureCoordinate).xy;gl_Position = aPosition;
}

工具类:

import android.content.Context;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.microedition.khronos.opengles.GL10;public class RawUtils {public static int createOESTextureObject() {int[] tex = new int[1];GLES20.glGenTextures(1, tex, 0);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);return tex[0];}public static String readShaderFromResource(Context context, int resourceId) {StringBuilder builder = new StringBuilder();InputStream is = null;InputStreamReader isr = null;BufferedReader br = null;try {is = context.getResources().openRawResource(resourceId);isr = new InputStreamReader(is);br = new BufferedReader(isr);String line;while ((line = br.readLine()) != null) {builder.append(line + "\n");}} catch (IOException e) {e.printStackTrace();} finally {try {if (is != null) {is.close();is = null;}if (isr != null) {isr.close();isr = null;}if (br != null) {br.close();br = null;}} catch (IOException e) {e.printStackTrace();}}return builder.toString();}
}

这里还需要一个显示中间框的自定义view,这里的大小位置直接写死了,可以根据需求更改:

/*** 显示照相机界面*/
public class PreviewBorderView extends SurfaceView implements SurfaceHolder.Callback, Runnable {private int mScreenH;private int mScreenW;private Canvas mCanvas;private Paint mPaint;private SurfaceHolder mHolder;private Thread mThread;public PreviewBorderView(Context context) {this(context, null);}public PreviewBorderView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public PreviewBorderView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}/*** 初始化绘图变量*/private void init() {this.mHolder = getHolder();this.mHolder.addCallback(this);this.mHolder.setFormat(PixelFormat.TRANSPARENT);setZOrderOnTop(true);this.mPaint = new Paint();this.mPaint.setAntiAlias(true);this.mPaint.setColor(Color.WHITE);this.mPaint.setStyle(Paint.Style.FILL_AND_STROKE);this.mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));setKeepScreenOn(true);}/*** 绘制取景框*/private void draw() {try {this.mCanvas = this.mHolder.lockCanvas();this.mCanvas.drawARGB(100, 0, 0, 0);float rate = (float)mScreenW / 1080;this.mCanvas.drawRect(new RectF(162*rate,  mScreenH-312*rate-1200*rate, mScreenW-162*rate, mScreenH-312*rate), this.mPaint);} catch (Exception e) {e.printStackTrace();} finally {if (this.mCanvas != null) {this.mHolder.unlockCanvasAndPost(this.mCanvas);}}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {//获得宽高,开启子线程绘图this.mScreenW = getWidth();this.mScreenH = getHeight();this.mThread = new Thread(this);this.mThread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {//停止线程try {mThread.interrupt();mThread = null;} catch (Exception e) {e.printStackTrace();}}@Overridepublic void run() {//子线程绘图draw();}
}

照相显示界面:

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;import java.io.ByteArrayOutputStream;import butterknife.BindView;
import butterknife.ButterKnife;public class CameraActivity extends Activity implements View.OnClickListener {@BindView(R.id.camera_take_photo_text_view)ImageView tradingCameraTakePhotoTextView;@BindView(R.id.camera_retry_text_view)TextView tradingCameraRetryTextView;@BindView(R.id.camera_sure_text_view)ImageView tradingCameraSureTextView;@BindView(R.id.camera_bottom_relative_layout)RelativeLayout tradingCameraBottomRelativeLayout;@BindView(R.id.camera_middle_image_view)ImageView tradingCameraMiddleImageView;@BindView(R.id.camera_hint_text_view)TextView tradingCameraHintTextView;@BindView(R.id.camera_close_image_view)ImageView tradingCameraCloseImageView;@BindView(R.id.trading_record_surface_view)CameraV2GLSurfaceView tradingRecordSurfaceView;@BindView(R.id.camera_bg_view)View tradingCameraBgView;private CameraV2 mCamera;private Bitmap mPhotoBitmap;private String mPath;public static Intent newIntent(Context context,String path){Intent intent=new Intent(context,CameraActivity.class);intent.putExtra("path",path);return intent;}@Overrideprotected void onCreate(Bundle savedInstanceState) {this.requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,  WindowManager.LayoutParams.FLAG_FULLSCREEN);hideBottomUIMenu();super.onCreate(savedInstanceState);mPath = getIntent().getStringExtra("path");setContentView(R.layout.activity_camera);ButterKnife.bind(this);initView();}private void initView() {tradingCameraTakePhotoTextView.setOnClickListener(this);tradingCameraRetryTextView.setOnClickListener(this);tradingCameraSureTextView.setOnClickListener(this);tradingCameraCloseImageView.setOnClickListener(this);mCamera = new CameraV2(this);mCamera.setupCamera(ScreenUtils.getScreenWidth(), ScreenUtils.getScreenHeight(),mPath);if (!mCamera.openCamera()) {return;}tradingRecordSurfaceView.init(mCamera, false, this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.camera_close_image_view:finish();break;case R.id.camera_take_photo_text_view:mCamera.capture(new CameraV2.CaptureCallBack() {@Overridepublic void photoData(Bitmap bitmap) {mPhotoBitmap = bitmap;tradingCameraMiddleImageView.setImageBitmap(bitmap);tradingCameraBgView.setVisibility(View.VISIBLE);tradingCameraRetryTextView.setVisibility(View.VISIBLE);tradingCameraSureTextView.setVisibility(View.VISIBLE);tradingCameraTakePhotoTextView.setVisibility(View.GONE);}});break;case R.id.camera_retry_text_view:tradingCameraMiddleImageView.setImageBitmap(null);tradingCameraRetryTextView.setVisibility(View.GONE);tradingCameraSureTextView.setVisibility(View.GONE);tradingCameraTakePhotoTextView.setVisibility(View.VISIBLE);tradingCameraBgView.setVisibility(View.GONE);mCamera.restartPreview();break;case R.id.camera_sure_text_view://bitmap不能直接回传图片过大,导致卡在页面Intent intent=new Intent();ByteArrayOutputStream baos = new ByteArrayOutputStream();mPhotoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);byte[] datas = baos.toByteArray();intent.putExtra("photo",datas);setResult(RESULT_OK,intent);finish();break;}}@Overrideprotected void onDestroy() {mCamera.closeCamera();super.onDestroy();}/*** 隐藏虚拟按键,并且全屏*/protected void hideBottomUIMenu() {//for new api versions.View decorView = getWindow().getDecorView();int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN;decorView.setSystemUiVisibility(uiOptions);}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><com.zqb.camera2.CameraV2GLSurfaceView
        android:id="@+id/trading_record_surface_view"android:layout_width="match_parent"android:layout_height="match_parent"/><com.zqb.camera2.PreviewBorderView
        android:layout_width="wrap_content"android:layout_height="wrap_content" /><View
        android:visibility="gone"android:id="@+id/camera_bg_view"android:background="#000"android:layout_width="match_parent"android:layout_height="match_parent"/><ImageView
        android:tint="#fff"android:id="@+id/camera_close_image_view"android:src="@mipmap/global_close_btn"android:layout_margin="72px"android:layout_width="60px"android:layout_height="60px" /><RelativeLayout
        android:id="@+id/camera_bottom_relative_layout"android:layout_marginBottom="48px"android:layout_marginTop="74px"android:layout_alignParentBottom="true"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageView
            android:id="@+id/camera_take_photo_text_view"android:layout_centerHorizontal="true"android:scaleType="fitXY"android:src="@drawable/selector_btn_take_photo"android:layout_width="188px"android:layout_height="188px" /><TextView
            android:id="@+id/camera_retry_text_view"android:visibility="gone"android:layout_marginLeft="264px"android:textSize="44px"android:text="重新拍照"android:layout_centerVertical="true"android:textColor="#fff"android:layout_width="wrap_content"android:layout_height="wrap_content" /><ImageView
            android:id="@+id/camera_sure_text_view"android:visibility="gone"android:layout_marginRight="380px"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:scaleType="fitXY"android:src="@drawable/selector_btn_take_photo_sure"android:layout_width="160px"android:layout_height="160px" /></RelativeLayout><ImageView
        android:scaleType="fitXY"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:layout_marginBottom="312px"android:id="@+id/camera_middle_image_view"android:layout_width="756px"android:layout_height="1200px"android:background="@mipmap/global_camera_bg"/><TextView
        android:id="@+id/camera_hint_text_view"android:layout_above="@id/camera_middle_image_view"android:layout_marginBottom="58px"android:layout_centerHorizontal="true"android:textSize="44px"android:text="请对准相框"android:textColor="#4448ed"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
</RelativeLayout>

最开始调用显示界面(布局就不贴了,就一个简单的imageview):

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;import butterknife.BindView;
import butterknife.ButterKnife;public class MainActivity extends AppCompatActivity {@BindView(R.id.image_view)ImageView imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);imageView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = CameraActivity.newIntent(MainActivity.this,null);startActivityForResult(intent,1);}});}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(requestCode==1 && resultCode==RESULT_OK){Bundle extras = data.getExtras();if(extras!=null) {byte[] photos = data.getByteArrayExtra("photo");Bitmap bitmap = BitmapFactory.decodeByteArray(photos, 0, photos.length);imageView.setImageBitmap(bitmap);}}}
}

注:
ColorMatrix和gl中RGB相加=1就为黑白,矩阵那可以随意更改只要保证RGB的三个横排相加等于1就可以,可以在这里自由设置显示不同效果。

Camera2实现带照相框的可修改显示效果的自定义照相机相关推荐

  1. typora将html转为格式,Typora 修改配置文件实现自定义标签样式(常用)

    Typora 修改配置文件实现自定义标签样式(常用) Typora 修改配置文件实现自定义标签样式(常用) 一.简介 Typora 是一款支持实时预览的 Markdown 文本编辑器.它系统自带了几种 ...

  2. 修改aapt和自定义资源ID

    源码查看 aapt的源码在所在的目录:Android/frameworks/base/tools/aapt/. Main.cpp位置:android-6.0.0_r1/frameworks/base/ ...

  3. 批量修改ip成自定义网址

    批量修改ip成自定义网址 针对ip后几位数修改成规定格式的网址 文本域进行添加多个ip <textarea rows="" cols="" id=&quo ...

  4. Carson带你学Android:源码解析自定义View Draw过程

    前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...

  5. WordPress批量添加、修改、删除自定义字段的sql命令

    写一下WordPress批量添加.修改.删除自定义字段的sql命令,今天有个小伙伴问我一个问题,原本他用的是另外一个主题,看见日主题比较美观,于是他换到了日主题,用日主题的时候遇到了个问题,凡是资源站 ...

  6. centos7自带数据库MariaDB重启和修改密码

    1:MariaDB和mysql差不多是mysql的一个分支,完全兼容mysql的命令. 2:centos 7 中自带MariaDB, 需要在centos中安装mysql的时候就需要多注意了. 3:启动 ...

  7. CE进阶操作--自带小游戏TutorialGame的修改方法

    一.背景 ​ 最近玩CE的时候,突然发现Help菜单里面居然还藏着一个小游戏,花了半天时间,终于使用CE完成了所有关卡,现在分享给大家,也可以和大家共同加深CE功能的学习. 二.环境 系统:windo ...

  8. css3搜索框呼出键盘,移动端 input 输入框实现自带键盘“搜索“功能并修改X

    主要利用html5的,input[type=search]属性来实现,此时input和type=text外观和功能没啥区别: html代码入下: 但要实现点击键盘右下角搜索,来发送请求,js代码如下( ...

  9. 谷粒学院-分页查询、条件查询带分页、添加、修改

    讲师分页查询 注意:也可以使用MyBatis分页插件PageHelper,这里不用 https://blog.csdn.net/ZHOU_VIP/article/details/121773800 1 ...

最新文章

  1. 相对最完整的软件测试工具手册
  2. jq 解决 动画 淡入淡出,隐藏显示,多次操作BUG
  3. Entity Framework 4.1(转)
  4. 【原创】MIPS中断系统的板级验证及实例测试
  5. SAP OData CSRF token的后台验证源代码
  6. TransR:实体和关系分开嵌入(知识图谱嵌入)2015 AAAI
  7. python多分类画roc曲线_利用python制作ROC曲线进行多分类
  8. Pycharm远程调试报错:undefined symbol: AttachDebuggerTracing
  9. PE 文件格式 详解 一
  10. Python 爬虫之 Requests 库
  11. mysql 添加添加事务处理
  12. 系统提示“无法删除文件,无法读取源文件或磁盘”的解决办法
  13. 51单片机WIFI模块ESP8266-01s一定要看这个!普中科技给的模块 避免采坑!+CWJAP:3 FAIL 配置不了wifi名称和密码?
  14. 2019年7款3D扫描仪APP(Android和iOS),让你手机秒变3D扫描仪!
  15. 数组求极值——Java
  16. 电大学前本计算机考试,2019最新电大学前儿童艺术教育(音乐)形成性考核册作业1-4答案【呕心沥血整理可直接打印.doc...
  17. linux查找文件夹名称
  18. udacity 学java_Udacity前端开发(入门)第一个月学习小结
  19. 阿里云免费服务器,学生可以申请免费6个月!
  20. 人生观,世界观,价值观树立的方式

热门文章

  1. 3dsmax安装后 应用程序无法正常启动(0xc0000022)。请单击确定 关闭。的解决办法
  2. 全球与中国LFP阴极粉末市场深度研究分析报告
  3. spf13-vim 介绍及常用快捷键
  4. webpack3.x模块化与自动化详解-张玉坤-专题视频课程
  5. wannafly挑战赛4C-水题思维-割草机
  6. 计算机系统之间数据互连方式,平板如何连接电脑实现数据传输?平板连接电脑的图文方法...
  7. Android开发-Activity中“android:exported“属性的作用,以及“Permission Denial: starting Intent“错误解决
  8. 计算机在学前教育和美术绘画中的应用,浅谈创意美术在学前教育美术教学中的作用...
  9. C# PropertyGrid 控件应用
  10. vue2组件系列:Slider 滑块