首页 > 代码库 > Android 实例讲解自定义Camera拍照和预览以及前后置摄像头切换
Android 实例讲解自定义Camera拍照和预览以及前后置摄像头切换
上一篇博文讲解了怎么去调用本地图片和调用系统拍照图片(http://blog.csdn.net/a123demi/article/details/40003695)的功能。
而本博文将通过实例实现自定义Camera的功效。具体功能如下:
1.实现自定义Camera拍照;
2.实现前后置摄像头的切换;
3.实现Camera拍照后图片缩小显示以及正常预览;
4.实现Camera拍照后图片保存;
在具体实现代码之前,我们先来了解一下Android api对实现自定义Camera的介绍。
根据api的介绍,对于Camera应用可以简单总结以下几个步骤。
1.检查Camera是否存在,并在AndroidManifest.xml中赋予相关的权限;
2.创建一个继承于SurfaceView并实现SurfaceHolder接口的Camera Preview类;
3.在2的基础上新建一个Camera Preview布局文件;
4.设置一个拍照的监听事件,例如单击按钮事件等;
5.实现拍照,并保存拍照后的图片到设备;
6.释放Camera,以方便其他应用可以使用。
下面将通过具体代码实现我们给出的三个功能。
一.相关的xml文件
1.AndroidManifest.xml相关配置以及相关权限,实现步骤一当中的权限配置
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.camerasurfacedemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.camerasurfacedemo.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.camerasurfacedemo.CameraActivity" ></activity> <activity android:name="com.example.camerasurfacedemo.PreviewActivity" ></activity> </application> </manifest>
2.activity_main.xml主函数入口,进入自定义Camera界面入口,实现拍照后图片缩小显示功能
<LinearLayout 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:gravity="center_horizontal" android:orientation="vertical" > " <Button android:id="@+id/id_go_camera_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="进入拍照界面" /> <ImageView android:id="@+id/id_show_camera_iv" android:layout_width="150dp" android:layout_height="200dp" android:gravity="center" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="拍照图片显示区域" android:textColor="#FF0000" android:textSize="20sp" /> </LinearLayout>
3.activity_camera.xml自定义Camera preview布局,实现步骤2,该界面实现前后置摄像头切换以及自定义拍照等
<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" > <LinearLayout android:id="@+id/id_process_btns_ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_alignParentTop="true" > <Button android:id="@+id/id_switch_camera_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="切换前后摄像头" /> <Button android:id="@+id/id_capture_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="拍照" /> </LinearLayout> <SurfaceView android:id="@+id/id_area_sv" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/id_process_btns_ll" android:text="拍照区域" /> </RelativeLayout>4.activity_preview.xml实现拍照后图片放大预览
<LinearLayout 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:background="#000000" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="拍照图片预览" android:textColor="#FF0000" android:textSize="20sp" /> <ImageView android:id="@+id/id_preview_camera_iv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" /> </LinearLayout>
二.java代码实现
1.帮助类HelpUtil.java
package com.example.camerasurfacedemo; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.net.Uri; public class HelpUtil { /** * 根据图片路径获取本地图片的Bitmap * * @param url * @return */ public static Bitmap getBitmapByUrl(String url) { FileInputStream fis = null; Bitmap bitmap = null; try { fis = new FileInputStream(url); bitmap = BitmapFactory.decodeStream(fis); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); bitmap = null; } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } fis = null; } } return bitmap; } /** * bitmap旋转90度 * * @param bitmap * @return */ public static Bitmap createRotateBitmap(Bitmap bitmap) { if (bitmap != null) { Matrix m = new Matrix(); try { m.setRotate(90, bitmap.getWidth() / 2, bitmap.getHeight() / 2);// 90就是我们需要选择的90度 Bitmap bmp2 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); bitmap.recycle(); bitmap = bmp2; } catch (Exception ex) { System.out.print("创建图片失败!" + ex); } } return bitmap; } public static Bitmap getBitmapByUri(Uri uri,ContentResolver cr){ Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeStream(cr .openInputStream(uri)); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); bitmap = null; } return bitmap; } /** * 获取格式化日期字符串 * @param date * @return */ @SuppressLint("SimpleDateFormat") public static String getDateFormatString(Date date) { if (date == null) date = new Date(); String formatStr = new String(); SimpleDateFormat matter = new SimpleDateFormat("yyyyMMdd_HHmmss"); formatStr = matter.format(date); return formatStr; } }
2.主函数类MainActivity.java
package com.example.camerasurfacedemo; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ImageView; public class MainActivity extends Activity { private Button goCameraBtn; private ImageView showCameraIv; private static final int CAMERA_CODE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); goCameraBtn = (Button)this.findViewById(R.id.id_go_camera_btn); goCameraBtn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { processGoCamera(); } }); showCameraIv = (ImageView)this.findViewById(R.id.id_show_camera_iv); showCameraIv.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { processShowCamera(v); } }); } /** * 处理进入camera事件 */ private void processGoCamera(){ Intent intent = new Intent(); intent.setClass(this, CameraActivity.class); startActivityForResult(intent,CAMERA_CODE); } /** * 处理图片跳转进入预览界面 */ private void processShowCamera(View v){ Intent intent = new Intent(); intent.setClass(this, PreviewActivity.class); /** * 将图片url传给PreviewActivity */ intent.putExtra("cameraUrl", v.getContentDescription().toString()); startActivity(intent); } @Override public void onActivityResult(int requestCode,int resultCode,Intent data){ super.onActivityResult(requestCode, resultCode, data); if(RESULT_OK == resultCode){ if(CAMERA_CODE == requestCode){ /** * 获取activity返回的url */ Uri uri = data.getData(); String url = uri.toString().substring(uri.toString().indexOf("///")+2); if(url != null && !TextUtils.isEmpty(url)){ showCameraIv.setContentDescription(url); showCameraIv.setImageBitmap(HelpUtil.getBitmapByUrl(url)); } } } } }
注意:这里通过startActivityForResult(intent,CAMERA_CODE)跳转和 onActivityResult(int requestCode,int resultCode,Intent data)返回拍照后的图片路径信息
3.自定义Camera preview类CameraActivity.java
package com.example.camerasurfacedemo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Date; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.hardware.Camera; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.Button; public class CameraActivity extends Activity implements OnClickListener, SurfaceHolder.Callback { private static final String TAG = CameraActivity.class.getSimpleName(); private static final int MEDIA_TYPE_IMAGE = 1; private Button switchCameraBtn, captureBtn; private SurfaceView surfaceSv; private SurfaceHolder mHolder; private Camera mCamera; // 0表示后置,1表示前置 private int cameraPosition = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 不显示标题 this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_camera); findById(); initData(); } /** * 初始化view */ private void findById() { switchCameraBtn = (Button) this.findViewById(R.id.id_switch_camera_btn); captureBtn = (Button) this.findViewById(R.id.id_capture_btn); surfaceSv = (SurfaceView) this.findViewById(R.id.id_area_sv); switchCameraBtn.setOnClickListener(this); captureBtn.setOnClickListener(this); } /** * 初始化相关data */ private void initData() { // 获得句柄 mHolder = surfaceSv.getHolder(); // 添加回调 mHolder.addCallback(this); } @Override public void onStart() { super.onStart(); if (this.checkCameraHardware(this) && (mCamera == null)) { // 打开camera mCamera = getCamera(); if (mHolder != null) { setStartPreview(mCamera,mHolder); } } } private Camera getCamera() { Camera camera = null; try { camera = Camera.open(); } catch (Exception e) { // Camera is not available (in use or does not exist) camera = null; Log.e(TAG, "Camera is not available (in use or does not exist)"); } return camera; } @Override public void onPause() { super.onPause(); /** * 记得释放camera,方便其他应用调用 */ releaseCamera(); } @Override public void onDestroy() { super.onDestroy(); } /** * 释放mCamera */ private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview();// 停掉原来摄像头的预览 mCamera.release(); mCamera = null; } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.id_switch_camera_btn: // 切换前后摄像头 int cameraCount = 0; CameraInfo cameraInfo = new CameraInfo(); cameraCount = Camera.getNumberOfCameras();// 得到摄像头的个数 for (int i = 0; i < cameraCount; i++) { Camera.getCameraInfo(i, cameraInfo);// 得到每一个摄像头的信息 if (cameraPosition == 1) { // 现在是后置,变更为前置 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { /** * 记得释放camera,方便其他应用调用 */ releaseCamera(); // 打开当前选中的摄像头 mCamera = Camera.open(i); // 通过surfaceview显示取景画面 setStartPreview(mCamera,mHolder); cameraPosition = 0; break; } } else { // 现在是前置, 变更为后置 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { /** * 记得释放camera,方便其他应用调用 */ releaseCamera(); mCamera = Camera.open(i); setStartPreview(mCamera,mHolder); cameraPosition = 1; break; } } } break; case R.id.id_capture_btn: // 拍照,设置相关参数 Camera.Parameters params = mCamera.getParameters(); params.setPictureFormat(ImageFormat.JPEG); params.setPreviewSize(800, 400); // 自动对焦 params.setFocusMode(Parameters.FOCUS_MODE_AUTO); mCamera.setParameters(params); mCamera.takePicture(null, null, picture); break; } } @Override public void surfaceCreated(SurfaceHolder holder) { setStartPreview(mCamera,mHolder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 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 setStartPreview(mCamera,mHolder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 当surfaceview关闭时,关闭预览并释放资源 /** * 记得释放camera,方便其他应用调用 */ releaseCamera(); holder = null; surfaceSv = null; } /** * 创建png图片回调数据对象 */ PictureCallback picture = 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: "); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); returnResult(pictureFile); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } }; /** * Create a File for saving an image or video */ private static File getOutputMediaFile(int type) { // 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 String timeStamp = HelpUtil.getDateFormatString(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".png"); } else { return null; } return mediaFile; } /** 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; } } /** * activity返回式返回拍照图片路径 * @param mediaFile */ private void returnResult(File mediaFile) { Intent intent = new Intent(); intent.setData(Uri.fromFile(mediaFile)); this.setResult(RESULT_OK, intent); this.finish(); } /** * 设置camera显示取景画面,并预览 * @param camera */ private void setStartPreview(Camera camera,SurfaceHolder holder){ try { camera.setPreviewDisplay(holder); camera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
注意:
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; } }
2.创建png格式的回调接口
/** * 创建png图片回调数据对象 */ PictureCallback picture = 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: "); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); returnResult(pictureFile); } catch (FileNotFoundException e) { Log.d(TAG, "File not found: " + e.getMessage()); } catch (IOException e) { Log.d(TAG, "Error accessing file: " + e.getMessage()); } } };
3.生成图片文件并保存
/** * Create a File for saving an image or video */ private static File getOutputMediaFile(int type) { // 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 String timeStamp = HelpUtil.getDateFormatString(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".png"); } else { return null; } return mediaFile; }
4.Camera一定要release
/** * 释放mCamera */ private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.stopPreview();// 停掉原来摄像头的预览 mCamera.release(); mCamera = null; } }
5.Activity结果返回
/** * activity返回式返回拍照图片路径 * @param mediaFile */ private void returnResult(File mediaFile) { Intent intent = new Intent(); intent.setData(Uri.fromFile(mediaFile)); this.setResult(RESULT_OK, intent); this.finish(); }
4.图片正常预览PreviewActivity.java
package com.example.camerasurfacedemo; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.view.Window; import android.widget.ImageView; import android.widget.Toast; public class PreviewActivity extends Activity { private ImageView previewCameraIv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_preview); previewCameraIv = (ImageView)this.findViewById(R.id.id_preview_camera_iv); Intent intent = this.getIntent(); String cameraUrl = intent.getStringExtra("cameraUrl").toString(); if(cameraUrl != null && !TextUtils.isEmpty(cameraUrl)){ previewCameraIv.setImageBitmap(HelpUtil.getBitmapByUrl(cameraUrl)); }else{ Toast.makeText(this, "图片路径错误", Toast.LENGTH_SHORT).show(); } } }
以上就是本博文的所有内容,谢谢品读。
源码路径:http://download.csdn.net/detail/a123demi/8029265
Android 实例讲解自定义Camera拍照和预览以及前后置摄像头切换