Android相机开发详解(一)
请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客)
Android相机开发能够实现打开相机,前后摄像头切换,摄像预览,保存图片,浏览已拍照图片等相机功能。
Android相机开发详解(一)主要实现打开相机,摄像预览,前后置摄像头切换,保存图片等四个功能。
Android相机开发详解(二)主要实现翻页浏览相片,触控缩放浏览图片,删除图片,发送图片等四个功能。
Android相机开发详解(三)主要实现录像,视频保存,自动对焦,闪光灯控制等四个功能
效果图:
1、 CameraActivity的布局文件,使用FrameLayout布局(activity_camera.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity" >
<FrameLayout
android:id="@+id/fragmentContainer_camera"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
2、 CameraActivity相机不存在的布局文件(activity_no_camera.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".CameraActivity" >
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="未检查到本地有摄像头" />
</RelativeLayout>
3、 CameraActivity类(CameraActivity.java)
a) 请求窗口特性:无标题
b) 添加窗口特性:全屏
c) 检查摄像头是否存在。根据检查结果进行布局
package com.example.camerademo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
public class CameraActivity extends FragmentActivity {
private final static int REQUEST_DELETE_PHOTO = 1;
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
//请求窗口特性:无标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
//添加窗口特性:全屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
//检查摄像头是否存在。
PackageManager pm = getPackageManager();
boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) ||
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
Build.VERSION.SDK_INT>Build.VERSION_CODES.GINGERBREAD ||
Camera.getNumberOfCameras()>0;
//根据检查结果进行布局
if (!hasCamera) {
setContentView(R.layout.activity_no_camera);
return;
}
setContentView(R.layout.activity_camera);
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
android.support.v4.app.Fragment fragment = fm.findFragmentById(R.id.fragmentContainer_camera);
if (fragment==null) {
fragment = new CameraFragment();
fm.beginTransaction().add(R.id.fragmentContainer_camera, fragment).commit();
}
}
}
4、 CameraFragment的布局文件(fragment_camera.xml)
a) 使用FrameLayout布局,双层布局
b) 顶层是一个SurfaceView,用来拍照预览
c) 底层是FrameLayout布局,包含两个ImageButton,一个ProgressBar进度条控件
<?xml version="1.0" encoding="UTF-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="@+id/camera_surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
<ImageButton
android:id="@+id/camera_rotationview_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|top"
android:layout_marginTop="16dp"
android:layout_marginLeft="16dp"
android:background="@drawable/button_camera_rotationview"
/>
<ImageButton
android:id="@+id/camera_takepicture_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|top"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp"
android:background="@drawable/button_camera_takepicture"
/>
<ImageButton
android:id="@+id/camera_view_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:background="@drawable/button_camera_view"
/>
<ProgressBar
android:id="@+id/camera_progressContainer"
style="@android:style/Widget.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</FrameLayout>
</FrameLayout>
5、 CameraFragment类(CameraFragment.java)
a) 相机是一种系统级别的重要资源,因此,很重要一点:需要时使用,用完及时释放。如果忘记释放,除非重启设备,否则其他应用将无法使用相机。
b) 保险起见,我们在onResume()方法中打开相机,在onPause()方法中释放相机。Camera类中打开相机的方法有两个。
i. open()方法在API第9级以下的级别使用。
ii. open(int)方法在第9级及第9级以上的级别使用,传入参数0开打设备可用的第一相机(通常指的是后置相机),传入参数1开打设备可用的第二相机(通常指的是前置相机)
c) 使用SurfaceView类配合相机来实现摄像预览。需要实现SurfaceHolder接口,并设置Surface类型:setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)。但该方法和该常量在API第11级别就已经被弃用。我们使用@SuppressWarnings("deprecation")注解来消除弃用代码的相关警告。
d) SurfaceHolder是我们与Surface对象联系的纽带。Surface对象代表着原始像素数据的缓冲区。SurfaceView出现在屏幕上时,会创建Surface,这时我们需要把Camera连接到SurfaceHolder上;SurfaceView从屏幕上消失时,Surface随机被销毁,我们再将Camera从SurfaceHolder断开。注意:Surface不存在时,必须保证没有任何内容要在它上面绘制。理想工作状态如图:
e) 为了完成以上任务,SurfaceHolder提供了另一个接口:SurfaceHolder.Callback。该接口有三个方法用来监听Surface生命周期中的事件。
i. 包含SurfaceView的视图层级结构被放到屏幕上时调用该方法。也是与Camera进行关联的地方。
public voidsurfaceCreated(SurfaceHolder surfaceHolder)
ii. Surface首次显示在屏幕上时调用该方法。通过传入的参数,可以知道Surface的像素格式以及它的宽度和高度。
public voidsurfaceChanged(SurfaceHolder holder, int format, int w, int h)
iii. SurfaceView从屏幕上移除时,Surface也随即被销毁。也是Camera与其断开关联的地方
public voidsurfaceDestroyed(SurfaceHolder holder)
f) 为了能与SurfaceView无缝贴合,Camera也提供了不同的方法。
i. 为了连接Camera和Surface,设置Surface被实时预览使用setPreviewDisplay(SurfaceHolder holder)
ii. 开始捕捉和绘制预览帧到屏幕上
iii. 停止捕捉和绘制预览帧到屏幕上
g) 使用Camera的内部类Camera.Parameters来确定预览图片和保存图片的大小
i. getParameters()返回这个相机的当前参数设置。
ii. setParameters(Camera.Parameters params)改变这个相机的当前参数设置。
iii. 然后自定义一个找出设备支持的最佳尺寸的方法getBestSupportedSize(List<Size> sizes,int width,int height),接受一组预览尺寸,然后穷举法找出具有最大数目像素的尺寸。
iv. 调用Camera.Parameters类的方法。
1. getSupportedPictureSizes()得到图片支持的尺寸
2. getSupportedPreviewSizes()得到预览图片支持的尺寸
3. setPictureSize(int width, int height)设置图片的大小尺寸
4. setPreviewSize(int width, int height)设置预览图片的大小尺寸
h) 使用Camera类见名知意的拍照方法takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback postview, Camera.PictureCallback jpeg)方法
i. 该方法会触发一个异步的图像捕捉,触发相机初始化一系列的回调机制应用于图像捕捉进程中
ii. 该方法有四个回调方法。Camera.ShutterCallback shutter在图像被捕捉时刻触发,它会触发一个快门声音告知用户;PictureCallback raw在未加工图像有效时触发;Camera.PictureCallback postview在被按比例缩放图像有效时触发;Camera.PictureCallback jpeg在压缩图像有效时触发。
iii. 该方法只有在 startPreview()方法调用之后才有效。该方法调用之后预览效果将会停止,如果想要再次预览或者拍摄更多相片,需要再次调用startPreview()方法。
package com.example.camerademo;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
public class CameraFragment extends Fragment{
//startActivityForResult的请求常量
private final static int REQUEST_DELETE_PHOTO = 1;
//自定义时间类
private MyTime mTime=new MyTime();
//相机类
private Camera mCamera;
//预览视图的接口
private SurfaceHolder mSurfaceHolder;
//进度条控件
private View mProgressContainer;
//当前打开的是哪一个摄像头
private int switchCamera=0;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@SuppressWarnings("deprecation")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
//生成fragment视图
View v = inflater.inflate(R.layout.fragment_camera, container,false);
//隐藏进度条控件
mProgressContainer = v.findViewById(R.id.camera_progressContainer);
mProgressContainer.setVisibility(View.INVISIBLE);
//显示最新照片的缩略图的按钮实例化
ImageButton viewButton = (ImageButton) v.findViewById(R.id.camera_view_button);
//最新照片的缩略图的按钮监听器
viewButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
// 跳转ViewPagerActivity,请求ViewPagerActivity执行删除图片操作
Intent i = new Intent();
i.setClass(getActivity(), ViewPagerActivity.class);
startActivityForResult(i, REQUEST_DELETE_PHOTO);
}
});
//切换镜头按钮实例化
ImageButton rotationViewButton = (ImageButton) v.findViewById(R.id.camera_rotationview_button);
//切换镜头按钮监听器
rotationViewButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
//如果摄像头数目小于等于1,该按钮无效,返回
if (Camera.getNumberOfCameras() <= 1) {
return ;
}
if(switchCamera == 1) {
//停掉原来摄像头的预览,并释放原来摄像头
mCamera.stopPreview();
mCamera.release();
mCamera = null;
//打开当前选中的摄像头
switchCamera = 0;
mCamera = Camera.open(switchCamera);
try {
//通过surfaceview显示取景画面
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//开始预览
mCamera.startPreview();
}else {
//停掉原来摄像头的预览,并释放原来摄像头
mCamera.stopPreview();
mCamera.release();
mCamera = null;
//打开当前选中的摄像头
switchCamera = 1;
mCamera = Camera.open(switchCamera);
try {
//通过surfaceview显示取景画面
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//开始预览
mCamera.startPreview();
}
}
});
//照相按钮实例化
ImageButton takePictureButton = (ImageButton) v.findViewById(R.id.camera_takepicture_button);
//照相按钮监听器
takePictureButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
if (mCamera!=null) {
//相机的拍照方法
mCamera.takePicture(
//第一个回调方法,快门回调方法
new ShutterCallback() {
@Override
public void onShutter() {
// TODO Auto-generated method stub
//该方法回触发快门声音告知用户,并设置进度条显示
mProgressContainer.setVisibility(View.VISIBLE);
}
}
//第二个,第三个回调方法为空
, null,null,
//最后一个回调方法,jpg图像回调方法
new PictureCallback() {
@Override
public void onPictureTaken(byte[] date, Camera camera) {
// TODO Auto-generated method stub
//根据当前时间自定义格式生成文件名
String filename = mTime.getYMDHMS()+".jpg";
//文件输出流
FileOutputStream os = null;
//默认文件保存成功
boolean success = true;
try {
//私有打开应用沙盒文件夹下文件
os = getActivity().openFileOutput(filename, Context.MODE_PRIVATE);
//写文件
os.write(date);
} catch (Exception e) {
// TODO: handle exception
success = false;
}finally{
try {
if (os != null) {
os.close();
}
} catch (Exception e) {
// TODO: handle exception
success = false;
}
}
if (success) {
//如果文件保存成功,进度条隐藏
mProgressContainer.setVisibility(View.INVISIBLE);
//再次预览
try {
mCamera.startPreview();
} catch (Exception e) {
// TODO: handle exception
mCamera.release();
mCamera=null;
}
}
}
});
}
}
});
//预览视图实例化
SurfaceView mSurfaceView = (SurfaceView) v.findViewById(R.id.camera_surfaceView);
//得到预览视图接口
mSurfaceHolder = mSurfaceView.getHolder();
//设置预览视图接口类型
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//添加预览视图接口的回调程序,监听视图的生命周期
mSurfaceHolder.addCallback(new Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
// TODO Auto-generated method stub
//当SurfaceView的视图层级结构被放到屏幕上时候,连接Camera和Surface
try {
if (mCamera!=null) {
mCamera.setPreviewDisplay(surfaceHolder);
}
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// TODO Auto-generated method stub
//当Surface首次显示在屏幕上时候,设置好相机参数,开始预览
if (mCamera==null) {
return;
}
Camera.Parameters parameters = mCamera.getParameters();
Size s = getBestSupportedSize(parameters.getSupportedPreviewSizes(), w, h);
parameters.setPreviewSize(s.width, s.height);
s = getBestSupportedSize(parameters.getSupportedPictureSizes(), w, h);
parameters.setPictureSize(s.width, s.height);
mCamera.setParameters(parameters);
try {
mCamera.startPreview();
} catch (Exception e) {
// TODO: handle exception
mCamera.release();
mCamera=null;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
//当视图从屏幕上移除的时候,停止预览
if (mCamera!=null) {
mCamera.stopPreview();
}
}
});
return v;
}
/******************************************]
*
* 穷举法找出具有最大数目像素的尺寸
*
* @param sizes
* @param width
* @param height
* @return
*/
public Size getBestSupportedSize(List<Size> sizes,int width,int height) {
Size bestSize = sizes.get(0);
int largestArea = bestSize.width*bestSize.height;
for (Size s :sizes) {
int area =s.width*s.height;
if (area>largestArea) {
bestSize=s;
largestArea = area;
}
}
return bestSize;
}
//接收活动结果,响应startActivityForResult()
@Override
public void onActivityResult(int request, int result, Intent mIntent) {
// TODO Auto-generated method stub
if (request == REQUEST_DELETE_PHOTO) {
if (result == Activity.RESULT_OK) {
// 跳转ViewPagerActivity,请求ViewPagerActivity执行删除图片操作
int requestCode = 1;
Intent i = new Intent();
i.setClass(getActivity(), ViewPagerActivity.class);
startActivityForResult(i, requestCode);
}
}
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
System.out.println("onPause");
//程序中止暂停时,释放Camera
if (mCamera!=null) {
mCamera.release();
mCamera=null;
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
System.out.println("onDestroy");
}
@Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
System.out.println("onStop");
}
@SuppressLint("NewApi")
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
//程序运行时,打开Camera
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(switchCamera);
}else {
mCamera = Camera.open();
}
}
}
6、 添加权限和Activity特性(AndroidMainfest.xml)
a) 权限:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android:hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
b) Activity特性:
android:screenOrientation="landscape"
请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijian的csdn博客)
学习《Android编程权威指南》心得与笔记 by2015.2.10早