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)     顶层亦是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()方法在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早

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。