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) 顶层亦是FrameLayout布局,包含一个ProgressBar进度条控件
c) 底层是LinearLayout布局,左边SurfaceView,右边两个ImageButton
<?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早