只是选择相机部分来翻译。下面是主要内容
有些开发者可能需要Camera的接口,来定制自己程序的外观和特殊功能。创建自定义的Camera界面比使用using an Intent需要编写更多的代码,但是它能提供更好的体验给用户。
创建自定义camera界面的一般步骤,可以参考一下步骤:
- 检测和连接相机—检测camera是否存在和请求连接
- 创建预览类—创建Preview,它继承SurfaceView和实现了SurfaceHolder接口。这个类可以预览相机中动态的画面。
- 构造预览的布局一旦你拥有了Preview类,就需要创建一个View布局。布局中放入Preview和你想要的其他UI。
- 给拍照设置监听器—连接监听器去启动拍照,来响应用户的动作,例如按下按钮。
- 拍照和保存文件—编写代码实现拍照和保存输出。
- 释放相机—使用camera后,程序必须合理的释放它,给其他程序使用。
相机硬件上一个共享的资源,必须被安全的管理,所以当使用相机时,你的程序不能和其他程序冲突。下面部分讲述怎样检测相机硬件,怎样连接相机,怎样拍照和当不使用时怎样释放相机。
注意:当应用程序不使用相机时,记得通过调用Camera.release()来释放Camera对象。如果没有合理释放camera,后面全部对camera的连接都会失败,将导致你的或其他程序被关闭。
检测相机硬件
如果你的程序没有在manifest声明需要相机,你应该在代码运行时检测相机是否可用。使用PackageManage.hasSystemFeature()方法来执行检测,下面是例子:
1 /** Check if this device has a camera */ 2 private boolean checkCameraHardware(Context context) { 3 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ 4 // this device has a camera 5 return true; 6 } else { 7 // no camera on this device 8 return false; 9 } 10 }
android设备可以用多个相机,例如后置相机用来拍照,前置相机用来摄像。Aandroid 2.3(API Level 9)和以后版本,你可以使用Camera.getNumberOfCameras()方法来检测相机的数目。
连接相机
如果你已经确定程序运行的设备上有相机,你必须请求连接来获取一个Camera实例(除非你使用intent来连接相机)。
使用Camera.opern()连接主要的相机,要确保捕获异常,就像下面的代码:
1 /** A safe way to get an instance of the Camera object. */ 2 public static Camera getCameraInstance(){ 3 Camera c = null; 4 try { 5 c = Camera.open(); // attempt to get a Camera instance 6 } 7 catch (Exception e){ 8 // Camera is not available (in use or does not exist) 9 } 10 return c; // returns null if camera is unavailable 11 }
注意:当使用Camera.open()时,总是要检查异常。如果不检查,当相机正在被使用或者不存在时,你的程序将会被系统关闭。
运行Android 2.3(API Level 9)或以上版本的设备,你可以使用Camera.open(int)连接特定的相机。上面的代码将会连接设备的第一个后置相机,如果存在多个相机。
检查相机特征
一旦你获取到相机的连接,你可以使用Camera.getParameters()方法获取有关它的功能的更多信息,检查返回的Camera.Parameters对象来知道相机支持的功能。
当使用API Level 9或以上的版本,使用Camera.getCameraInfo()确定相机是前置还是后置,图片的方向。
创建预览类
为了用户能有效地拍照,他们必须能够看到设备相机看到的。一个相机预览类是一个SurfaceView,它能展示来自相机的动态的图像数据,所以用户可以能拍摄到照片。
下面的例子展示了如何创建一个基本的相机预览类,它被包含在一个View布局中。这个类实现了SurfaceHolder.Callback接口,为了能捕捉到创建和销毁这个类时的回调事件。
1 /** A basic Camera preview class */ 2 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 3 private SurfaceHolder mHolder; 4 private Camera mCamera; 5 6 public CameraPreview(Context context, Camera camera) { 7 super(context); 8 mCamera = camera; 9 10 // Install a SurfaceHolder.Callback so we get notified when the 11 // underlying surface is created and destroyed. 12 mHolder = getHolder(); 13 mHolder.addCallback(this); 14 // deprecated setting, but required on Android versions prior to 3.0 15 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 16 } 17 18 public void surfaceCreated(SurfaceHolder holder) { 19 // The Surface has been created, now tell the camera where to draw the preview. 20 try { 21 mCamera.setPreviewDisplay(holder); 22 mCamera.startPreview(); 23 } catch (IOException e) { 24 Log.d(TAG, "Error setting camera preview: " + e.getMessage()); 25 } 26 } 27 28 public void surfaceDestroyed(SurfaceHolder holder) { 29 // empty. Take care of releasing the Camera preview in your activity. 30 } 31 32 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 33 // If your preview can change or rotate, take care of those events here. 34 // Make sure to stop the preview before resizing or reformatting it. 35 36 if (mHolder.getSurface() == null){ 37 // preview surface does not exist 38 return; 39 } 40 41 // stop preview before making changes 42 try { 43 mCamera.stopPreview(); 44 } catch (Exception e){ 45 // ignore: tried to stop a non-existent preview 46 } 47 48 // set preview size and make any resize, rotate or 49 // reformatting changes here 50 51 // start preview with new settings 52 try { 53 mCamera.setPreviewDisplay(mHolder); 54 mCamera.startPreview(); 55 56 } catch (Exception e){ 57 Log.d(TAG, "Error starting camera preview: " + e.getMessage()); 58 } 59 } 60 }
如果你想为相机预览类设定一个特定的大小,在上面的surfaceChaned()方法中设定。当设定大小时,你必须从getSupportedPreviewSizes()获取数值。不要随意设定一个值。
放置preview入一个布局中
相机预览类,如同上面展示的,它必须被放置进Activity的布局中,这样其他UI可以控制拍照。下面一节展示了如何为这个preview创建一个基本的布局和Activity。
下面的布局代码提供了一个非常基本的view来展示相机预览类。在这个例子中,FrameLayout元素就是相机预览类的容器。使用这个布局类型,可以把另外的图片信息或者控制覆盖在动态相机预览图像上。
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="horizontal" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 > 7 <FrameLayout 8 android:id="@+id/camera_preview" 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent" 11 android:layout_weight="1" 12 /> 13 14 <Button 15 android:id="@+id/button_capture" 16 android:text="Capture" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:layout_gravity="center" 20 /> 21 </LinearLayout>
在大多数设备中,相机预览默认的方向是横向的。这个例子设定了水平布局,下面的代码中修改了应用程序的方向为横向。为了使相机预览类渲染简单,你应该应该把照着下面在manifest中把应用程序预览所在的ctivity为横向。
1 <activity android:name=".CameraActivity" 2 android:label="@string/app_name" 3 4 android:screenOrientation="landscape"> 5 <!-- configure this activity to use landscape orientation --> 6 7 <intent-filter> 8 <action android:name="android.intent.action.MAIN" /> 9 <category android:name="android.intent.category.LAUNCHER" /> 10 </intent-filter> 11 </activity>
注意:相机预览不一定要是横向的。从android 2.2(API 8)开始,你可以使用setDisplayOrientation()方法去旋转预览的图像。当用户改变了手机方向,如果要调整预览的方向,可以在预览类的surfaceChanged()方法中,先使用Camer.stopPreview()停止预览,然后改变方向,最后再使用Camera.startPreview()打开预览.
在你相机视图所在的activity中,下面的例子中,添加了预览类到FrameLayout元素中。这个activity必须确保当它被暂停(paused)或者关闭时,要释放相机。下面的例子,展示了如何向activity添加预览类(在创建预览类一节提到的)。
1 public class CameraActivity extends Activity { 2 3 private Camera mCamera; 4 private CameraPreview mPreview; 5 6 @Override 7 public void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.main); 10 11 // Create an instance of Camera 12 mCamera = getCameraInstance(); 13 14 // Create our Preview view and set it as the content of our activity. 15 mPreview = new CameraPreview(this, mCamera); 16 FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); 17 preview.addView(mPreview); 18 } 19 }
注意:getCameraInstance()方法,是在连接相机一节的。
拍照
一旦你构建了预览类(preview)和一个展示它的视图布局,你将要在你的应用程序中开始编写拍照了。你必须设置监听器给你的UI去响应用户的拍照动作。
为了获取照片,使用Camera.takePicture()方法。这个方法需要三个参数,它们从camera中获取数据。为了获取JPEG格式的数据,你必须实现一个Camera.PictureCallback接口来获取图像数据,并且写入一个文件中。
下面的代码展示了一个基本的实现,如何保存来自camera的图像。
1 private PictureCallback mPicture = new PictureCallback() { 2 3 @Override 4 public void onPictureTaken(byte[] data, Camera camera) { 5 6 File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE); 7 if (pictureFile == null){ 8 Log.d(TAG, "Error creating media file, check storage permissions: " + 9 e.getMessage()); 10 return; 11 } 12 13 try { 14 FileOutputStream fos = new FileOutputStream(pictureFile); 15 fos.write(data); 16 fos.close(); 17 } catch (FileNotFoundException e) { 18 Log.d(TAG, "File not found: " + e.getMessage()); 19 } catch (IOException e) { 20 Log.d(TAG, "Error accessing file: " + e.getMessage()); 21 } 22 } 23 };
通过Camera.takePicture()触发拍照。下面的例子展示了如何通过button的View.OnClickListener调用此方法。
1 // Add a listener to the Capture button 2 Button captureButton = (Button) findViewById(id.button_capture); 3 captureButton.setOnClickListener( 4 new View.OnClickListener() { 5 @Override 6 public void onClick(View v) { 7 // get an image from the camera 8 mCamera.takePicture(null, null, mPicture); 9 } 10 } 11 );
提示:mPicture成员变量来自上面的例子。
注意:记得当你的应用程序停止使用Camera对象时通过Camera.release()释放它。如何释放camera的细节,情况释放相机一节。
释放相机
相机作用一种资源,被设备中的多个应用程序共享。你的应用程序在获取Camera实例后可以使用它,当你的程序停止使用它时,或者一旦你的程序暂停(Activity.onPause()),你必须特别注意去释放它。如果你的程序没有适当的释放它,所有后续对它的连接请求,都会导致你的程序或者其他程序都会被关闭。
使用Camera.release()方法释放一个Camera对象的实例,下面是例子。
1 public class CameraActivity extends Activity { 2 private Camera mCamera; 3 private SurfaceView mPreview; 4 5 ... 6 7 @Override 8 protected void onPause() { 9 super.onPause(); 10 releaseCamera(); // release the camera immediately on pause event 11 } 12 13 private void releaseCamera(){ 14 if (mCamera != null){ 15 mCamera.release(); // release the camera for other applications 16 mCamera = null; 17 } 18 } 19 }
参考:http://developer.android.com/guide/topics/media/camera.html