}
});
}
@Override
public void openCamera(int facing, Activity activity, SurfaceTexture surfaceTexture) {
postTask(new Runnable() {
@Override
public void run() {
camera.openCamera(facing, activity, surfaceTexture);
}
});
}
@Override
public void closeCamera() {
postTask(new Runnable() {
@Override
public void run() {
camera.closeCamera();
}
});
}
@Override
public void switchCamera() {
postTask(new Runnable() {
@Override
public void run() {
camera.switchCamera();
}
});
}
@Override
public void switchCamera(int facing) {
postTask(new Runnable() {
@Override
public void run() {
camera.switchCamera(facing);
}
});
}
@Override
public void setCameraFacing(int facing) {
postTask(new Runnable() {
@Override
public void run() {
camera.setCameraFacing(facing);
}
});
}
@Override
public int getCameraFacing() {
return camera.getCameraFacing();
}
@Override
public void setPreviewSize(Size cameraSize) {
postTask(new Runnable() {
@Override
public void run() {
camera.setPreviewSize(cameraSize);
}
});
}
@Override
public Size getPreviewSize() {
return camera.getPreviewSize();
}
@Override
public void setDisplayOrientation(int displayOrientation) {
postTask(new Runnable() {
@Override
public void run() {
camera.setDisplayOrientation(displayOrientation);
}
});
}
@Override
public int getDisplayOrientation() {
return camera.getDisplayOrientation();
}
@Override
public void releaseCamera() {
if (handler == null) {
return;
}
postTask(new Runnable() {
@Override
public void run() {
camera.releaseCamera();
stopBackground();
}
});
}
@Override
public void addOnCameraListener(OnCameraListener onCameraListener) {
postTask(new Runnable() {
@Override
public void run() {
camera.addOnCameraListener(onCameraListener);
}
});
}
@Override
public void removeOnCameraListener(OnCameraListener onCameraListener) {
postTask(new Runnable() {
@Override
public void run() {
camera.removeOnCameraListener(onCameraListener);
}
});
}
@Override
public void removeAllOnCameraListener() {
postTask(new Runnable() {
@Override
public void run() {
camera.removeAllOnCameraListener();
}
});
}
/**
- 获取Handler
*/
private Handler getHandler() {
if (thread == null || handler == null) {
startBackground();
}
return handler;
}
/**
- 开启线程
*/
private void startBackground() {
stopBackground();
thread = new HandlerThread(“camera_manager”);
thread.start();
handler = new Handler(thread.getLooper());
}
/**
- 关闭线程
*/
private void stopBackground() {
if (thread != null) {
thread.quitSafely();
thread = null;
}
if (handler != null) {
handler = null;
}
}
/**
- 执行任务
*/
private void postTask(Runnable runnable) {
getHandler().post(runnable);
}
}
注意到,CameraManager
内部开启了Handler
,目的是让相机的相关操作都在子线程进行
接下来可以具体实现Camera1
同样,我们新建Camera1
,并实现ICamera
接口
三、Camera1
全局变量
/**
- Camera
/
private Camera camera;
/* - 预览尺寸
/
private Size cameraSize = new Size(-1, -1);
/* - 默认摄像头方向
/
private int facing = CameraConstants.facing.BACK;
/* - 旋转角度
*/
private int displayOrientation = -1;
打开相机
public void openCamera(int facing, Activity activity, SurfaceTexture surfaceTexture) {
// 先关闭相机
closeCamera();
// 判断是否存在摄像头
int cameraNum = Camera.getNumberOfCameras();
if (cameraNum <= 0) {
onCameraError(new IllegalStateException(“camera num <= 0”));
return;
}
// 检查传入的facing
int cameraIndex = -1;
if (facing == CameraConstants.facing.BACK) {
cameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK;
} else if (facing == CameraConstants.facing.FRONT) {
cameraIndex = Camera.CameraInfo.CAMERA_FACING_FRONT;
}
if (cameraIndex == -1) {
onCameraError(new IllegalStateException(“camera facing exception”));
return;
}
// 判断摄像头个数,以决定使用哪个打开方式
if (cameraNum >= 2) {
camera = Camera.open(cameraIndex);
} else {
camera = Camera.open();
}
// 判断Camera是否初始化成功
if (camera == null) {
onCameraError(new IllegalStateException(“camera is null”));
return;
}
this.facing = facing;
try {
// 获取摄像头参数
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
if (cameraSize.getWidth() <= 0 || cameraSize.getHeight() <= 0) {
Camera.Size size = findTheBestSize(previewSizeList,
activity.getResources().getDisplayMetrics().widthPixels,
activity.getResources().getDisplayMetrics().heightPixels);
cameraSize.setWidth(size.width);
cameraSize.setHeight(size.height);
}
// 设置预览尺寸
parameters.setPreviewSize(cameraSize.getWidth(), cameraSize.getHeight());
// 这里设置使用的对焦模式
if (this.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
// 设置摄像头参数
camera.setParameters(parameters);
camera.setPreviewTexture(surfaceTexture);
if (displayOrientation < 0) {
displayOrientation = CameraUtils.getDisplayOrientation(activity, cameraIndex);
}
camera.setDisplayOrientation(displayOrientation);
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
onCameraOpened(cameraSize, Camera1.this.facing);
}
});
camera.startPreview();
} catch (IOException e) {
onCameraError(e);
}
}
注意到,在对相机参数设置的时候,使用findTheBestSize
方法进行选择最佳的尺寸,该方法不是Android
原生提供的,是根据相机所支持的所有预览尺寸和当前屏幕的尺寸所计算得到的
private Camera.Size findTheBestSize(List<Camera.Size> sizeList, int screenW, int screenH) {
if (sizeList == null || sizeList.isEmpty()) {
throw new IllegalArgumentException();
}
Log.d(TAG, “findTheBestSize: screenW:” + screenW + " screenH:" + screenH);
Camera.Size bestSize = sizeList.get(0);
for (Camera.Size size : sizeList) {
int width = size.height;
int height = size.width;
Log.d(TAG, “findTheBestSize: width:” + width + " height:" + height);
float ratioW = (float) width / screenW;
float ratioH = (float) height / screenH;
Log.d(TAG, “findTheBestSize: ratioW:” + ratioW + " ratioH:" + ratioH);
if (ratioW == ratioH) {
bestSize = size;
break;
}
}
return bestSize;
}
还注意到,设置相机旋转角度时,有用到CameraUtils.getDisplayOrientation
方法,该方法大概长这样
public static int getDisplayOrientation(Activity activity, int facing) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(facing, info);
int rotation = activity.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;
}
return result;
}
关闭相机
public void closeCamera() {
if (camera == null) {
return;
}
camera.stopPreview();
camera.release();
camera = null;
onCameraClosed();
}
切换相机
public void switchCamera() {
if (facing == CameraConstants.facing.BACK) {
facing = CameraConstants.facing.FRONT;
} else {
facing = CameraConstants.facing.BACK;
}
}
注意到,切换相机方法并没有真正的去切换相机,而是改变全局变量facing
的值,需要配合使用openCamera
方法后,进行切换相机的操作
接下来就是真正实现相机预览
四、TextureView
可以配合TextureView使用,进行相机相关操作
首先在xml中定义TextureView
<?xml version="1.0" encoding="utf-8"?><TextureView
android:id=“@+id/textureView”
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:
他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。
刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。
技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!
提升自己去挑战一下BAT面试难关吧
对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
不论遇到什么困难,都不应该成为我们放弃的理由!
如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!
最后祝各位新人都能坚持下来,学有所成。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
有大量大厂面试题为你面试做准备!**
提升自己去挑战一下BAT面试难关吧
[外链图片转存中…(img-KWEW1Kom-1712608809945)]
对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
不论遇到什么困难,都不应该成为我们放弃的理由!
如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!
最后祝各位新人都能坚持下来,学有所成。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!