Android camera用来拍照和拍摄视频的先看一下最后实现的效果图
最后的效果图
在你的应用程序上使用android拍照设备,需要考虑以下几个方面
如果需要,那么就无法安装在没有摄像头的设备。
需要在mainfest 中声明
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
可以有两种方式
2.1 简单的拍照 使用已经存在camera apps
2.2 需要更多定制的功能,创建一个camera app
是只能在本应用程序里使用还是可以被其他程序使用。
android.hardware.camera2
包中包含控制设备相机的基本类
Camera
过时不用的相机控制类
SurfaceView
提供实时的相机预览
MediaRecorder
用来记录video
Intent
使用
MediaStore.ACTION_IMAGE_CAPTURE
MediaStore.ACTION_VIDEO_CAPTURE
两个Intent类型来捕获图像和视频
camera许可
<uses-permission android:name="android.permission.CAMERA" />
camera特征
<uses-feature android:name="android.hardware.camera" />
存储许可
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
音视频录制
<uses-permission android:name="android.permission.RECORD_AUDIO" />
位置许可
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
使用方式是用Intent,使用Intent能够使用最少的代码来获取图片
流程主要是
创建一个camera Intent,Intent的类型包括,MediaStore.ACTION_IMAGE_CAPTURE
,MediaStore.ACTION_VIDEO_CAPTURE
启动一个camera Intent,使用startActivityForResult()方法来执行一个camera intent,在启动Intent后,camera应用界面能够显示在设备屏幕来供使用
接受Intent的结果,使用onActivityResult()方法来接受Intent的回调和数据。当用户结束拍照后,系统调用这一方法。
下列代码显示如何创建一个camera intent并执行
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100; private Uri fileUri; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // create Intent to take a picture and return control to the calling application Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE); // create a file to save the image intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file name // start the image capture Intent startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE); }
接收camera intent的结果,主要是重载onActivityResult。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100; private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) { if (resultCode == RESULT_OK) { // Image captured and saved to fileUri specified in the Intent Toast.makeText(this, "Image saved to:\n" + data.getData(), Toast.LENGTH_LONG).show(); } else if (resultCode == RESULT_CANCELED) { // User cancelled the image capture } else { // Image capture failed, advise user } } }
!一但程序接受到一个成功的结果,那么应用程序就可以获取在指定路径下的图片。
官方建议使用新的android.hardware.camera2,使用旧的Camera API会出现deprecated的警告。
一般创建一个定制Camera接口的步骤包括
1.检测和获取camera
/** Check if this device has a camera */ private boolean checkCameraHardware(Context context) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ // this device has a camera return true; } else { // no camera on this device return false; } }
/** A safe way to get an instance of the Camera object. */ public static Camera getCameraInstance(){ Camera c = null; try { c = Camera.open(); // attempt to get a Camera instance } catch (Exception e){ // Camera is not available (in use or does not exist) } return c; // returns null if camera is unavailable }
2.创建一个预览类
/** A basic Camera preview class */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; public CameraPreview(Context context, Camera camera) { 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.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 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 exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
3.创建一个预览的布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <FrameLayout android:id="@+id/camera_preview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <Button android:id="@+id/button_capture" android:text="Capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout>
public class CameraActivity extends Activity { private Camera mCamera; private CameraPreview mPreview; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Create an instance of Camera mCamera = getCameraInstance(); // Create our Preview view and set it as the content of our activity. mPreview = new CameraPreview(this, mCamera); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(mPreview); } }
4.开始监听捕获事件
private PictureCallback mPicture = new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); if (pictureFile == null){ Log.d(TAG, "Error creating media file, check storage permissions: " + e.getMessage()); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
5.捕获并保存文件
// Add a listener to the Capture button Button captureButton = (Button) findViewById(id.button_capture); captureButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // get an image from the camera mCamera.takePicture(null, null, mPicture); } } );
保存图片到SD卡中
private PictureCallback mPicture = new PictureCallback() { /** Create a File for saving an image or video */ @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(); if (pictureFile == null) { Log.d(TAG,"Error creating media file, check storage permissions: "); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } camera.startPreview(); } }; private File getOutputMediaFile() { // To be safe, you should check that the SDCard is mounted // using Environment.getExternalStorageState() before doing this. File mediaStorageDir = new File( 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 exist if (!mediaStorageDir.exists()) { if (!mediaStorageDir.mkdirs()) { Log.d("MyCameraApp", "failed to create directory"); return null; } } // Create a media file name File mediaFile; String timeStamp = String.format("%d.jpg", System.currentTimeMillis()); mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"); return mediaFile; }
6.释放相机
public class CameraActivity extends Activity { private Camera mCamera; private SurfaceView mPreview; private MediaRecorder mMediaRecorder; ... @Override protected void onPause() { super.onPause(); releaseMediaRecorder(); // if you are using MediaRecorder, release it first releaseCamera(); // release the camera immediately on pause event } private void releaseCamera(){ if (mCamera != null){ mCamera.release(); // release the camera for other applications mCamera = null; } } }
caution:
相机是一个共享资源,所以每次使用完毕后要记得调用Camera.release()来释放,否则后续想要获取相机的应用都会被关闭。
六、注意问题
6.1 拍完成照片后,停在拍照完成的界面,无法继续拍摄
在onPictureTaken的实现最后添加一个启动预览camera.startPreview();
private PictureCallback mPicture = new PictureCallback() { /** Create a File for saving an image or video */ @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(); if (pictureFile == null) { Log.d(TAG,"Error creating media file, check storage permissions: "); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } camera.startPreview(); } };
代码:
https://github.com/dawnminghuang/android_camera
参考资料
http://developer.android.com/guide/topics/media/camera.html