1.工程项目结构解析
java:我们写Java代码的地方,业务功能都在这里实现
res:存放我们各种资源文件的地方,有图片,字符串,动画,音频等,还有各种形式的XML文件。
2.代码解析
🕐 CameraActivity.java
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.camera2basic;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
// 想调用摄像头,必须继承自 OpenCV 的 CameraActivity
public class CameraActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
if (null == savedInstanceState) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, Camera2BasicFragment.newInstance())
.commit();
}
}
}
🕑 AutoFitTextureView.java
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.camera2basic;
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
/**
* A {@link TextureView} that can be adjusted to a specified aspect ratio.
* 自定义预览控件
*/
public class AutoFitTextureView extends TextureView {
private int mRatioWidth = 0;
private int mRatioHeight = 0;
public AutoFitTextureView(Context context) {
this(context, null);
}
public AutoFitTextureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
* calculated from the parameters. Note that the actual sizes of parameters don't matter, that
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
*
* @param width Relative horizontal size
* @param height Relative vertical size
*
*设置此视图的纵横比。视图的大小将根据比率进行测量
*/
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
}
该文件 添加一个约束宽高比的方法,设定宽高比后请求系统重新计算布局,实现预览窗口大小的调整。
经过上述改造,切换相机输出尺寸时,预览窗口会自动调整大小,保持和输出尺寸有相同的宽高比,这样就不会再出现图像拉伸的问题。
而作为重点的就是
Camera2BasicFragment的拍照流程:
1.进入相机,获取TextureView对象,然后开启一个后台线程处理相机数据
2.判断TextureView是否有效,有效就直接openCamera(),无效就加入一个监听SufaceTextureListener,通过回调确保surfaceTexture有效,然后同样openCamera()。
3.设置相机特性setUpCameraOutputs(),设置图片存储监听OnImageAvaliableListener,拍照图片有效会通知ImageSaver线程保存图片,设置AE,AF等。
4.设置矩阵变换 configureTransform()
5.获取CameraManager对象,然后真正通过manager.openCamera打开相机
6.打开相机成功的话,获取CameraDevice,createCameraPreviewSession()用来创建会话,
获得CaptureRequest对象,通过CaptureRequest发送重复请求捕捉画面,开启预览。
7.拍照流程:首先有个入口,拍照按钮点击事件,触发takePicture(),takePicture中实现了lockFocus()锁住焦点。
8.lockFocus():设置了一个等待锁定的状态,发送一次请求,加入了一个回调CaptureCallback,这一步还没有进行拍照。
9.在CaptureCallback的process()中,状态切换为STATE_WAITING_LOCK,进行拍照,当然不一定就是该状态下进行拍照,还要对AE,AF的状态进行判断,最后不管哪个状态下,都会调用captureStillPicture()进行拍照。
10.captureStillPicture() 设置拍照捕捉请求,设置成像方向与预览方向一致,中断停止预览的重复请求,最终进行拍照,拍照数据会由imageSaver处理,保存到文件,然后通过CameraCaptureSession.CaptureCallback回调解除锁定,回复预览界面
Camera API2类的关系
- 获取系统服务 CameraManager 实例
- 调用 CameraManager.getCameraCharacteristics() 方法获取 CameraCharacteristics 实例
- 调用 CameraManager.openCamera() 方法的传入 CameraDevice.StateCallback 方法中获取 CameraDevice 实例
- 调用 CameraDevice.createCaptureRequest() 方法创建请求会话实例 CaptureRequest.Builder , TEMPLATE_PREVIEW(预览请求), TEMPLATE_STILL_CAPTURE(拍照请求)
- 调用 CaptureRequest.Builder.addTarget(Surface) 设置 camera2 图像数据的目标 Surface
- 调用 CameraDevice.createCaptureSession() 创建相机捕获会话,传入 CameraCaptureSession.StateCallback 方法用于拍照或预览
- 在 CameraCaptureSession.StateCallback 中调用 CaptureRequest.Builder.build() 获取请求会话和 CameraCaptureSession 捕获会话
- 在 CameraCaptureSession.CaptureCallback 中实现两个抽象方法, onCaptureProgressed(捕获处理)和 onCaptureCompleted(捕获完成)
- 调用 CameraCaptureSession 发送 CaptureRequest 请求, capture:表示只发一次请求(拍照), setRepeatingRequest:表示不断发送请求(预览)
- 在 ImageReader.OnImageAvailableListener 监听中回调获取拍照/预览数