关于相机功能列表,请参考功能引用。增加相机功能到你的mainfest文件,这样Google Play可以阻止那些没有相机硬件或者没有相机特定功能的设备安装你的应用。关于Google Play如何做过滤的信息,请参考Google Play and Feature-Based Filtering。
你还可以为每个相机特性设置android:required的属性,表示这个功能是否为必须的。
下面的示例代码演示了如何构建一个拍照Intent并执行它。getOutputMediaFileUri()方法可以从Saving Media Files的段落中涉及到。
1234567891011121314151617
privatestaticfinalintCAPTURE_IMAGE_ACTIVITY_REQUEST_CODE=100;privateUrifileUri;@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// create Intent to take a picture and return control to the calling applicationIntentintent=newIntent(MediaStore.ACTION_IMAGE_CAPTURE);fileUri=getOutputMediaFileUri(MEDIA_TYPE_IMAGE);// create a file to save the imageintent.putExtra(MediaStore.EXTRA_OUTPUT,fileUri);// set the image file name// start the image capture IntentstartActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);}
当startActivityForResult()方法被执行,用户会看到一个相机拍照的界面。用户执行了拍照(或者取消操作),用户界面会回退到你的程序,你必须在onActivityResult()方法里面接收返回的数据。关于如何接受完整的intent,可以参考下面的Receiving camera intent result段落。
privatestaticfinalintCAPTURE_VIDEO_ACTIVITY_REQUEST_CODE=200;privateUrifileUri;@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);//create new IntentIntentintent=newIntent(MediaStore.ACTION_VIDEO_CAPTURE);fileUri=getOutputMediaFileUri(MEDIA_TYPE_VIDEO);// create a file to save the videointent.putExtra(MediaStore.EXTRA_OUTPUT,fileUri);// set the image file nameintent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,1);// set the video image quality to high// start the Video Capture IntentstartActivityForResult(intent,CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);}
privatestaticfinalintCAPTURE_IMAGE_ACTIVITY_REQUEST_CODE=100;privatestaticfinalintCAPTURE_VIDEO_ACTIVITY_REQUEST_CODE=200;@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){if(requestCode==CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE){if(resultCode==RESULT_OK){// Image captured and saved to fileUri specified in the IntentToast.makeText(this,"Image saved to:\n"+data.getData(),Toast.LENGTH_LONG).show();}elseif(resultCode==RESULT_CANCELED){// User cancelled the image capture}else{// Image capture failed, advise user}}if(requestCode==CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE){if(resultCode==RESULT_OK){// Video captured and saved to fileUri specified in the IntentToast.makeText(this,"Video saved to:\n"+data.getData(),Toast.LENGTH_LONG).show();}elseif(resultCode==RESULT_CANCELED){// User cancelled the video capture}else{// Video capture failed, advise user}}}
/** Check if this device has a camera */privatebooleancheckCameraHardware(Contextcontext){if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){// this device has a camerareturntrue;}else{// no camera on this devicereturnfalse;}}
/** A safe way to get an instance of the Camera object. */publicstaticCameragetCameraInstance(){Camerac=null;try{c=Camera.open();// attempt to get a Camera instance}catch(Exceptione){// Camera is not available (in use or does not exist)}returnc;// returns null if camera is unavailable}
/** A basic Camera preview class */publicclassCameraPreviewextendsSurfaceViewimplementsSurfaceHolder.Callback{privateSurfaceHoldermHolder;privateCameramCamera;publicCameraPreview(Contextcontext,Cameracamera){super(context);mCamera=camera;// Install a SurfaceHolder.Callback so we get notified when the// underlying surface is created and destroyed.mHolder=getHolder();mHolder.addCallback(this);// deprecated setting, but required on Android versions prior to 3.0mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);}publicvoidsurfaceCreated(SurfaceHolderholder){// The Surface has been created, now tell the camera where to draw the preview.try{mCamera.setPreviewDisplay(holder);mCamera.startPreview();}catch(IOExceptione){Log.d(TAG,"Error setting camera preview: "+e.getMessage());}}publicvoidsurfaceDestroyed(SurfaceHolderholder){// empty. Take care of releasing the Camera preview in your activity.}publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intw,inth){// If your preview can change or rotate, take care of those events here.// Make sure to stop the preview before resizing or reformatting it.if(mHolder.getSurface()==null){// preview surface does not existreturn;}// stop preview before making changestry{mCamera.stopPreview();}catch(Exceptione){// ignore: tried to stop a non-existent preview}// set preview size and make any resize, rotate or// reformatting changes here// start preview with new settingstry{mCamera.setPreviewDisplay(mHolder);mCamera.startPreview();}catch(Exceptione){Log.d(TAG,"Error starting camera preview: "+e.getMessage());}}}
<activityandroid:name=".CameraActivity"android:label="@string/app_name"android:screenOrientation="landscape"><!-- configure this activity to use landscape orientation --><intent-filter><actionandroid:name="android.intent.action.MAIN"/><categoryandroid:name="android.intent.category.LAUNCHER"/></intent-filter></activity>
publicclassCameraActivityextendsActivity{privateCameramCamera;privateCameraPreviewmPreview;@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// Create an instance of CameramCamera=getCameraInstance();// Create our Preview view and set it as the content of our activity.mPreview=newCameraPreview(this,mCamera);FrameLayoutpreview=(FrameLayout)findViewById(R.id.camera_preview);preview.addView(mPreview);}}
privatePictureCallbackmPicture=newPictureCallback(){@OverridepublicvoidonPictureTaken(byte[]data,Cameracamera){FilepictureFile=getOutputMediaFile(MEDIA_TYPE_IMAGE);if(pictureFile==null){Log.d(TAG,"Error creating media file, check storage permissions: "+e.getMessage());return;}try{FileOutputStreamfos=newFileOutputStream(pictureFile);fos.write(data);fos.close();}catch(FileNotFoundExceptione){Log.d(TAG,"File not found: "+e.getMessage());}catch(IOExceptione){Log.d(TAG,"Error accessing file: "+e.getMessage());}}};
触发拍照的动作,需要使用下面演示到的方法。
1234567891011
// Add a listener to the Capture buttonButtoncaptureButton=(Button)findViewById(id.button_capture);captureButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){// get an image from the cameramCamera.takePicture(null,null,mPicture);}});
publicclassCameraActivityextendsActivity{privateCameramCamera;privateSurfaceViewmPreview;privateMediaRecordermMediaRecorder;...@OverrideprotectedvoidonPause(){super.onPause();releaseMediaRecorder();// if you are using MediaRecorder, release it firstreleaseCamera();// release the camera immediately on pause event}privatevoidreleaseMediaRecorder(){if(mMediaRecorder!=null){mMediaRecorder.reset();// clear recorder configurationmMediaRecorder.release();// release the recorder objectmMediaRecorder=null;mCamera.lock();// lock camera for later use}}privatevoidreleaseCamera(){if(mCamera!=null){mCamera.release();// release the camera for other applicationsmCamera=null;}}}
publicstaticfinalintMEDIA_TYPE_IMAGE=1;publicstaticfinalintMEDIA_TYPE_VIDEO=2;/** Create a file Uri for saving an image or video */privatestaticUrigetOutputMediaFileUri(inttype){returnUri.fromFile(getOutputMediaFile(type));}/** Create a File for saving an image or video */privatestaticFilegetOutputMediaFile(inttype){// To be safe, you should check that the SDCard is mounted// using Environment.getExternalStorageState() before doing this.FilemediaStorageDir=newFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"MyCameraApp");// This location works best if you want the created images to be shared// between applications and persist after your app has been uninstalled.// Create the storage directory if it does not existif(!mediaStorageDir.exists()){if(!mediaStorageDir.mkdirs()){Log.d("MyCameraApp","failed to create directory");returnnull;}}// Create a media file nameStringtimeStamp=newSimpleDateFormat("yyyyMMdd_HHmmss").format(newDate());FilemediaFile;if(type==MEDIA_TYPE_IMAGE){mediaFile=newFile(mediaStorageDir.getPath()+File.separator+"IMG_"+timeStamp+".jpg");}elseif(type==MEDIA_TYPE_VIDEO){mediaFile=newFile(mediaStorageDir.getPath()+File.separator+"VID_"+timeStamp+".mp4");}else{returnnull;}returnmediaFile;}
// get Camera parametersCamera.Parametersparams=mCamera.getParameters();List<String>focusModes=params.getSupportedFocusModes();if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)){// Autofocus mode is supported}
如果你的程序确定需要相机的某个特性,你可以在mainfest文件中就进声明。例如你声明了flash与auto-focus的功能,那么Google Play会阻止那些不支持这些功能的设备安装这个应用。关于相机功能的声明列表,请参考Features Reference.
6.2)Using camera features
前面已经提到过,通过Camera.Parameters对象来操控相机。如下所示:
123456
// get Camera parametersCamera.Parametersparams=mCamera.getParameters();// set the focus modeparams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);// set Camera parametersmCamera.setParameters(params);
// Create an instance of CameramCamera=getCameraInstance();// set Camera parametersCamera.Parametersparams=mCamera.getParameters();if(params.getMaxNumMeteringAreas()>0){// check that metering areas are supportedList<Camera.Area>meteringAreas=newArrayList<Camera.Area>();RectareaRect1=newRect(-100,-100,100,100);// specify an area in center of imagemeteringAreas.add(newCamera.Area(areaRect1,600));// set weight to 60%RectareaRect2=newRect(800,-1000,1000,-800);// specify an area in upper right of imagemeteringAreas.add(newCamera.Area(areaRect2,400));// set weight to 40%params.setMeteringAreas(meteringAreas);}mCamera.setParameters(params);
publicvoidstartFaceDetection(){// Try starting Face DetectionCamera.Parametersparams=mCamera.getParameters();// start face detection only *after* preview has startedif(params.getMaxNumDetectedFaces()>0){// camera supports face detection, so can start it:mCamera.startFaceDetection();}}
如果使用前面4.4)Creating a preview class段落中提到的预览类,你需要在surfaceCreated() 与 surfaceChanged()方法中执行startFaceDetection()方法。如下所示:
publicvoidsurfaceCreated(SurfaceHolderholder){try{mCamera.setPreviewDisplay(holder);mCamera.startPreview();startFaceDetection();// start face detection feature}catch(IOExceptione){Log.d(TAG,"Error setting camera preview: "+e.getMessage());}}publicvoidsurfaceChanged(SurfaceHolderholder,intformat,intw,inth){if(mHolder.getSurface()==null){// preview surface does not existLog.d(TAG,"mHolder.getSurface() == null");return;}try{mCamera.stopPreview();}catch(Exceptione){// ignore: tried to stop a non-existent previewLog.d(TAG,"Error stopping camera preview: "+e.getMessage());}try{mCamera.setPreviewDisplay(mHolder);mCamera.startPreview();startFaceDetection();// re-start face detection feature}catch(Exceptione){// ignore: tried to stop a non-existent previewLog.d(TAG,"Error starting camera preview: "+e.getMessage());}}
Time lapse video使得用户可以通过组合一段时间的图片生成视频片段。这个功能利用了MediaRecorder每隔一段时间来记录一张图片。
为了实现这个功能,你需要像配置一个普通的recorder对象一样。如下面的代码所示:
12345
// Step 3: Set a CamcorderProfile (requires API Level 8 or higher)mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH));...// Step 5.5: Set the video capture rate to a low numbermMediaRecorder.setCaptureRate(0.1);// capture a frame every 10 seconds