自定义camera需要注意这几点:
在自定义相机之前可以看下这篇文章,了解一下相机传感器的方向问题https://blog.csdn.net/c10WTiybQ1Ye3/article/details/78098459
在解决详解预览的角度问题,官方有一个推荐的写法。
/**
* 保证预览方向正确
*
* @param context
* @param cameraId
* @param camera
*/
public void setCameraDisplayOrientation(Activity context,
int cameraId, Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = context.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
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;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
在预览是如果宽高设置的不恰当不仅可能会出现崩溃的情况,还会出现预览界面变形的情况,为了解决变形的情况,我们应该让预览界面的宽高比例和textureview的宽高比例尽量达到一致,同时在解决因为宽高设置的不恰当问题,我们也应该在相机支持的分辨率中去寻找。我为了更方便的控制宽高的比例,我设置的textureview的宽高是充满屏幕的。那么屏幕的宽高比例,就是相机分辨率的宽高比例。注意:因为相机传感器是横屏安装的,所以应该用屏幕的h/w,来确定设置的分辨率的w/h
/**
*
*
* @param sizes 相机支持的size
* @param targetRatio h/w
* @param comparator 升序或者降序
* @param minWidth 最小的支持宽度
* @return
*/
public Camera.Size getOptimalPreviewSize(List sizes, float targetRatio, int comparator, int minWidth) {
if (sizes == null)
return null;
Camera.Size optimalSize = null;
// 对size进行排序
Collections.sort(sizes, getComparator(comparator));
//先找出宽度大于最小宽度的size
List tempList = new ArrayList<>();
for(Camera.Size size : sizes){
Log.i("sss", "....width.....:"+size.width+"...height.."+size.height);
if(size.width >= minWidth){
tempList.add(size);
}
}
if(tempList.size() > 0){
// 找比例相同的,这里是整个屏幕的高和宽的比。
for (Camera.Size size : tempList) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - targetRatio == 0) {
optimalSize = size;
break;
}
}
}else{
//比例相同的
for (Camera.Size size : sizes) {
float currentRatio = ((float) size.width) / size.height;
if (currentRatio - targetRatio == 0) {
optimalSize = size;
break;
}
}
}
// 如果没有就找个相近的
if(optimalSize == null){
float tempRation;
float minRation = Float.MAX_VALUE;
if(tempList.size() > 0){
for (Camera.Size size : tempList) {
float curRatio = ((float) size.width) / size.height;
tempRation = Math.abs(targetRatio - curRatio);
if(tempRation
拍照之后图片旋转角度,对于图片的处理可以参考这片文章https://blog.csdn.net/Lamphogani/article/details/79197015?utm_source=blogxgwz9
我想要的要效果是图片的方向跟着手机的方向来设定,因此我使用的是
orientationEventListener = new OrientationEventListener(context) {
@Override
public void onOrientationChanged(int orientation) {
if (ORIENTATION_UNKNOWN == orientation) {
return;
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
orientation = (orientation + 45) / 90 * 90;
rotation = 0;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (info.orientation - orientation + 360) % 360;
} else {
rotation = (info.orientation + orientation) % 360;
}
// 我不知道怎么回事,我的这个设置并没起到在拍照之后图片旋转的效果
if (null != mCamera) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRotation(rotation);
mCamera.setParameters(parameters);
}
}
};
我只是在这记录拍照是的手机的角度,之后对图片进行的旋转
/**
* 把相机拍照返回照片转正
*
* @param angle 旋转角度
* @return bitmap 图片
*/
public Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
//旋转图片 动作
Matrix matrix = new Matrix();
matrix.postRotate(angle);
//加入翻转 把相机拍照返回照片转正
if (id == 1) {
matrix.postScale(-1, 1);
}
// 创建新的图片, 如果传入的角度是bitmap当前的角度的话,
// 就不会做重新生成一个新的bitmap。只是把bitmap的值给了resizeBitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
if(resizedBitmap == null){
resizedBitmap = bitmap;
}
return resizedBitmap;
}
注意到以上几个问题就差不多了,剩下的就是具体的自定义了。
首先做简单的设置,因为我使用的是camera+textureview,所以应该开启窗口加速,我在setcontentview中设置了
//此行代码必须存在,是TextureView必要在窗口加速中才能使用
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
requestWindowFeature(Window.FEATURE_NO_TITLE); //设置无标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //设置全屏
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮
//设置手机屏幕朝向,一共有7种
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
然后就是权限的申请,同时也注册了textureView.setSurfaceTextureListener(this);在权限申请通过和textureview创建成功之后,就可以初始化camera同时开启camera的预览了
` // 权限
String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
};`
初始化camera
`/**
*
* @param surfaceTexture
* @param cameraId
*/
@Override
public void startPreview(SurfaceTexture surfaceTexture, int cameraId) {
this.cameraId = cameraId;
this.mSurfaceTexture = surfaceTexture;
if (mCamera == null && mSurfaceTexture != null) {
if(orientationEventListener != null){
orientationEventListener.enable();
}
// 获取camera实例对象
mCamera = getCameraInstance(cameraId);
// 开启预览
try {
if (mCamera != null) {
mCamera.setPreviewTexture(mSurfaceTexture);
mCamera.lock();
setCameraDisplayOrientation(context, cameraId, mCamera);
initCameraParameters();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/** 安全获取Camera对象实例的方法 */
private Camera getCameraInstance(int cameraId) {
try {
mCamera = Camera.open(cameraId); // 试图获取Camera实例
}
catch (Exception e) {
// 摄像头不可用(正被占用或不存在)
}
return mCamera; // 不可用则返回null
}
` /**
* 初始化摄像头参数
*/
private void initCameraParameters() {
// 初始化摄像头参数
mParameters = mCamera.getParameters();
mCamera.lock();
List focusModes = mParameters.getSupportedFocusModes();
// 设置对焦模式
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// Autofocus mode is supported 自动对焦
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
if(focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)){
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
}
// 预览尺寸
previewSize = getOptimalPreviewSize(mParameters.getSupportedPreviewSizes(), DisplayUtils.getScreenRate(context), Constants.COMPARATOR_ASCEND, 1280);
if (previewSize != null) {
mParameters.setPreviewSize(previewSize.width, previewSize.height);
}
// 图片尺寸
Camera.Size pictrueSize = getOptimalPreviewSize(mParameters.getSupportedPictureSizes(), DisplayUtils.getScreenRate(context), Constants.COMPARATOR_ASCEND, 1280);
if (pictrueSize != null) {
mParameters.setPictureSize(pictrueSize.width, pictrueSize.height);
}
//设置预览格式
mParameters.setPreviewFormat(ImageFormat.NV21);
try {
mCamera.setParameters(mParameters);
mCamera.setPreviewCallback(mRecordingUtils);
mCamera.startPreview();
// 2如果要实现连续的自动对焦,这一句必须加上
mCamera.cancelAutoFocus();
} catch (Exception e) {
e.printStackTrace();
}
}
`
最后就是camera的拍照操作了。。。拍照有两个方法,一个是从camera.setPreviewCallback的监听中,获取拍照时的流,然后生成图片,另一种就是通过camera的takePicture方法获取。我使用的是第二种。虽然后面也的使用第一种监听中的方法进行视屏的录制。
mCamera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(final byte[] data, Camera camera) {
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
//进行了图片的旋转,因为是耗时所以我放在了子线程中去做
Bitmap savebitmap = rotaingImageView(cameraId, tempRotation, bitmap);
String img_path = imagePath;
if(TextUtils.isEmpty(img_path)){
img_path = Constants.DEFAULT_DIRECTORY;
}
img_path = img_path + "/" + System.currentTimeMillis() + ".jpeg";
File file = BitmapUtils.saveJPGE_After(context, savebitmap, img_path, 100);
if(cameraResultCallBack != null){
cameraResultCallBack.takePhotoResult(file);
}
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
}
if(savebitmap != null && !savebitmap.isRecycled()){
savebitmap.recycle();
savebitmap = null;
}
}
}).start();
mCamera.startPreview();
}
});
``
以上就是自定义camera的基本操作流程