/*
* 这个demon演示了如何利用照相机取景,并将实现图像的预览功能。
* 需要用到两个类,一个是Camera:照相机,用来管理和操作摄像头。
* 一个是SurfaceView用来显示照相机取到的图像。SurfaceView允许在非UI线程中在其表面
* 绘制图像,这样就避免了当绘制复杂的图像时会发生UI线程阻塞的问题。
*/
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
// 定义照相机
private Camera mCamera;
// 定义界面视图
// 照相机的数量
int numberOfCameras;
// 当时锁定的照相机
int cameraCurrentlyLocked;
// 默认照相机的ID
int defaultCameraId;
private Preview mpreView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置显示模式为全屏、无标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 1.设定应用的主界面,在这里我们自定义了一个视图容器,在这个容器中我们就放置了一个
// SurfaceView组件用于显示在摄像头中取到的图像。
mpreView=new Preview(this);
setContentView(mpreView);
//获取摄像头的数量
numberOfCameras=Camera.getNumberOfCameras();
//System.out.println(numberOfCameras);
//获取默认摄像头的ID
CameraInfo info=new CameraInfo();
for(int i=0;i<numberOfCameras;i++){
Camera.getCameraInfo(i, info);
if(info.facing==CameraInfo.CAMERA_FACING_BACK){
defaultCameraId=i;
}
}
}
@Override
protected void onResume() {
super.onResume();
//设置为横屏显示否则图像方向不正确
if(getRequestedOrientation()!=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
//打开并设置摄像头
mCamera=Camera.open();
cameraCurrentlyLocked=defaultCameraId;
mpreView.setCamera(mCamera);
}
@Override
protected void onPause() {
super.onPause();
if(mCamera!=null){
mpreView.setCamera(null);
mCamera.release();
mCamera=null;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater=getMenuInflater();
inflater.inflate(R.menu.camera_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.camera_menu:
if(numberOfCameras==1){
AlertDialog.Builder builder=new Builder(this);
builder.setMessage("只有一个摄像头!")
.setNeutralButton("close", null)
.show();
return true;
}
//释放掉当前的照相机
if(mCamera!=null){
mCamera.stopPreview();
mpreView.setCamera(null);
mCamera.release();
mCamera=null;
}
//切换到一个新的相机
mCamera=Camera.open((cameraCurrentlyLocked+1)%numberOfCameras);
cameraCurrentlyLocked=(cameraCurrentlyLocked+1)%numberOfCameras;
mpreView.switchCamera(mCamera);
mCamera.startPreview();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// 定义一个视图容器,作为应用的主界面。这也是这个Demo中最核心的部分。
private class Preview extends ViewGroup implements Callback {
// 定义用于显示图像的SurfaceView
private SurfaceView mSurfaceView;
// 定义一个用于控制和管理SurfaceView的句柄,照相机是通过使用SurfaceHolder句柄来
// 使用SurfaceView的。SurfaceHolder通过回调接口实现对SurfaceView的操作和管理
private SurfaceHolder mHolder;
// 定义预览图像的尺寸,该类位于android.hardware.Camera.Size;是Camera的一个内部类
// 是照相机用于控制和管理一些和图像的大小相关操作的类。
private Size mPreviewSize;
// 照相机支持的所有的预览尺寸
private List<Size> mSupportedPreviewSizes;
@SuppressWarnings("deprecation")
/*
* 使用照相机需要添加权限: <uses-permission
* android:name="android.permission.CAMERA" /> <uses-feature
* android:name="android.hardware.camera" /> <uses-feature
* android:name="android.hardware.camera.autofocus" />
*/
private Camera camera;
public Preview(Context context) {
super(context);
// 创建一个SurfaceView并将其添加到容器当中
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
// 从SurfaceView当中取得控制句柄
mHolder = mSurfaceView.getHolder();
// 为SurfaceHolder添加回调方法,用于控制和管理SurfaceView
mHolder.addCallback(this);
// 设置SurfaceHolder的类型表示由Camera管理缓存中的图像,实现图像预览
// 不过这个方法已经过时,当需要时应用会自动添加
// mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
@SuppressWarnings("deprecation")
public void setCamera(Camera camera) {
this.camera = camera;
if (camera != null) {
mSupportedPreviewSizes = camera.getParameters()
.getSupportedPreviewSizes();
//System.out.println(mSupportedPreviewSizes.size());
// 通过View布局被修改了,需要使修改立即生效。
requestLayout();
}
}
// 切换摄像头的方法
public void switchCamera(Camera camera) {
setCamera(camera);
// 显示图像的预览
try {
camera.setPreviewDisplay(mHolder);
} catch (IOException e) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", e);
}
// 修改照相机的参数设置
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
camera.setParameters(parameters);
}
/*
* 重写onMeasure方法,用于测量容器的尺寸,这里我们并不关心子组件的大小,因为我们只需要居中显示照相机的预览图像即可
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 该方法将根据不同的模式,从两个参数中选择一个合适的尺寸返回
int width = resolveSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
int height = resolveSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
/*System.out.println("width:"+width);
System.out.println("height:"+height);*/
// 设置容器的尺寸
setMeasuredDimension(width, height);
// 获取当前使用的预览尺寸(最合适的)
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes,
width, height);
}
}
private Size getOptimalPreviewSize(List<Size> sizes,
int width, int height) {
//设置精确度
final double ASPECT_TOLERANCE=0.1;
//定义容器的宽高比
double targetRatio=width/height;
if(sizes==null){
return null;
}
Size optimalSize=null;
double minDiff=Double.MAX_VALUE;
int targetHeight=height;
for(Size size:sizes){
double ratio=size.width/size.height;
//如果误差值大于设定的精度,则结束当前循环进入下一次循环
if(Math.abs(ratio-targetRatio)>ASPECT_TOLERANCE){
continue;
}
if(Math.abs(size.height-targetHeight)<minDiff){
optimalSize=size;
minDiff=Math.abs(size.height-targetHeight);
}
}
//如果找不到满足精度要求的,则忽视对精度的判断
if(optimalSize==null){
minDiff=Double.MAX_VALUE;
for(Size size:sizes){
if(Math.abs(size.height-targetHeight)<minDiff){
optimalSize=size;
minDiff=Math.abs(size.height-targetHeight);
}
}
}
return optimalSize;
}
/*
* 重写onLayout方法设定窗口的布局,因为在该窗口中我们只有一个SurfaceView
* 只需要让它在窗口中居中显示就可以了
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if(changed && getChildCount()>0){
View child=getChildAt(0);
//获取窗口的宽和高
int width=right-left;
int height=bottom-top;
child.layout(left, top, right, bottom);
int previewWidth=width;
int previewHeight=height;
if(mPreviewSize!=null){
previewWidth=mPreviewSize.width;
previewHeight=mPreviewSize.height;
}
/*System.out.println("width:"+width);
System.out.println("height:"+height);
System.out.println("previewWidth:"+previewWidth);
System.out.println("previewHeight:"+previewHeight);*/
//如果实际的预览视图的高度大于根据窗口的高宽比计算出的预览视图的高度
//就需要缩小预览视图以适应容器的大小。此时将高度设置到容器的高度,宽度就会变小
//就需要对宽度进行居中显示
if(previewHeight>(height/width)*previewWidth){
int scaledChildWidth=previewWidth*height/previewHeight;
child.layout((width-scaledChildWidth)/2, 0, (width+scaledChildWidth)/2, height);
//System.out.println("scaledChildWidth:"+scaledChildWidth);
}else{
//注意一定要把乘放在前面否则得到的结果为0
int scaledChildHeight=previewHeight*width/previewWidth;
System.out.println("scaledChildHeight:"+scaledChildHeight);
child.layout(0, (height-scaledChildHeight)/2, width, (height+scaledChildHeight)/2);
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// 设置照相机的预览视图
if(camera!=null){
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.e(TAG, "IOException caused by setPreviewDisplay()", e);
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Camera.Parameters params=camera.getParameters();
params.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
camera.setParameters(params);
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(camera!=null){
camera.stopPreview();
}
}
}
}
04-25
04-25
04-25
04-25
“相关推荐”对你有帮助么?
-
非常没帮助
-
没帮助
-
一般
-
有帮助
-
非常有帮助
提交