Android相机开发详解(一)

Android相机开发详解(一)

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

 

 

Android相机开发能够实现打开相机,前后摄像头切换,摄像预览,保存图片,浏览已拍照图片等相机功能。

Android相机开发详解(一)主要实现打开相机,摄像预览,前后置摄像头切换,保存图片等四个功能。

Android相机开发详解(二)主要实现翻页浏览相片,触控缩放浏览图片,删除图片,发送图片等四个功能。

Android相机开发详解(三)主要实现录像,视频保存,自动对焦,闪光灯控制等四个功能

效果图:

Android相机开发详解(一)_第1张图片

 

1、 CameraActivity的布局文件,使用FrameLayout布局(activity_camera.xml)



    




2、 CameraActivity相机不存在的布局文件(activity_no_camera.xml)



    




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进度条控件

 


 
	    
	 

	
	 
	      
	    
		    
	    
		
		    
		
		     
			    
			
	

    

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不存在时,必须保证没有任何内容要在它上面绘制。理想工作状态如图:

 Android相机开发详解(一)_第2张图片

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 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 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)      权限:



 
  
  
b)     Activity特性: 

android:screenOrientation="landscape"

 

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

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

 

你可能感兴趣的:(Android,android,摄像头,surface,相机,图片预览)