当前位置: 代码迷 >> Android >> Android照相机开发详解(一)
  详细解决方案

Android照相机开发详解(一)

热度:102   发布时间:2016-04-28 02:19:08.0
Android相机开发详解(一)

Android相机开发详解(一)

请支持原创,尊重原创,转载请注明出处:http://blog.csdn.net/kangweijian(来自kangweijiancsdn博客)

 

 

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()方法在API9级以下的级别使用。

               ii.         open(int)方法在第9级及第9级以上的级别使用,传入参数0开打设备可用的第一相机(通常指的是后置相机),传入参数1开打设备可用的第二相机(通常指的是前置相机)

c)      使用SurfaceView类配合相机来实现摄像预览。需要实现SurfaceHolder接口,并设置Surface类型:setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)。但该方法和该常量在API11级别就已经被弃用。我们使用@SuppressWarnings("deprecation")注解来消除弃用代码的相关警告。

d)     SurfaceHolder是我们与Surface对象联系的纽带。Surface对象代表着原始像素数据的缓冲区。SurfaceView出现在屏幕上时,会创建Surface,这时我们需要把Camera连接到SurfaceHolder上;SurfaceView从屏幕上消失时,Surface随机被销毁,我们再将CameraSurfaceHolder断开。注意: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.         为了连接CameraSurface,设置Surface被实时预览使用setPreviewDisplay(SurfaceHolder holder)

               ii.         开始捕捉和绘制预览帧到屏幕上

startPreview()

              iii.         停止捕捉和绘制预览帧到屏幕上

stopPreview()

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(来自kangweijiancsdn博客)

                                学习《Android编程权威指南》心得与笔记                    by2015.2.10早

 

1楼cheyiliu3天前 20:57
mark
Re: kangweijian前天 13:47
回复cheyiliunwhat?
  相关解决方案