Android Camera学习总结:如何在android中使用摄像头获取照片
这学期补修了Android这门课,短短的八次课让我对Android有了初步的了解。作为结课项目,老师让我们用Camera/Surfaceview完成相机功能。现将学习的心得记录下来。
整个相机程序实现的思路是,使用Camera实例,设置好参数后,得到摄像头传回的图像数据,将这些数据在Surfaceview实例中进行展示,实现预览功能。在Surfaceview下,设置click button,当单击click button后,调用Camera.takePicture方法,完成照相动作,将图片保存到手机本地。
整个工程的核心是实现了SurfaceHolder.Callback接口,重写接口中的几个方法,而这几个方法,都是Surfaceview实例将要调用的关键方法。他们分别是:surfaceCreated/surfaceDestroyed/surfaceChanged。然后可以通过Surfaceview.getHolder().addCallback(Callback)方法,将这个Callback与前端layout中的surfaceview相关联起来。
下面来看下Callback中方法具体代码:
1 @Override 2 public void surfaceChanged(SurfaceHolder holder, int format, int width, 3 int height) { 4 Log.e("tag", " surfaceChanged"); 5 mParameters = camera.getParameters(); 6 mParameters.setPictureFormat(PixelFormat.JPEG); 7 Log.e("tag", 8 "parameters.getPictureSize()" 9 + mParameters.getPictureSize().width); 10 setPictureSize(mParameters); 11 Log.i("tag", "holder width:" + width + " height:" + height); 12 // parameters.setPreviewSize(width, height);//需要判断支持的预览 13 14 camera.setParameters(mParameters); 15 16 camera.startPreview(); 17 } 18 19 private void setPictureSize(Parameters parameters) { 20 List<Size> sizes = parameters.getSupportedPictureSizes(); 21 if (sizes == null) { 22 return; 23 } 24 int maxSize = 0; 25 int width = 0; 26 int height = 0; 27 for (int i = 0; i < sizes.size(); i++) { 28 Size size = sizes.get(i); 29 int pix = size.width * size.height; 30 if (pix > maxSize) { 31 maxSize = pix; 32 width = size.width; 33 height = size.height; 34 } 35 } 36 Log.i("tag", "图片的大小:" + width + " height:" + height); 37 parameters.setPictureSize(width, height); 38 }
上段代码重写了surfaceChanged方法。该方法中完成了设置相机的参数,包括图片格式,预览尺寸,并开始预览功能,即在surfaceview中实时显示摄像头中捕捉的画面。其中setPicturesize方法使用了相对的图片大小设置,如果将图片大小设置成绝对值,可能会造成不同尺寸屏幕的机器不能正常运行程序的异常发生。
在另外两个方法中分别完成了camera实例的获取和销毁工作,具体代码如下:
@Override public void surfaceDestroyed(SurfaceHolder holder) { Log.i("tag", " surfaceDestroyed"); if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } } @Override public void surfaceCreated(SurfaceHolder holder) { try { if (camera == null) { camera = Camera.open(); } camera.setPreviewDisplay(holder); Log.i("", "camera created"); //WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); //Display display = wm.getDefaultDisplay(); // Camera.Parameters parameters = camera.getParameters(); // //parameters.setPreviewSize(display.getWidth(), display.getHeight());//设置预览照片的大小 // parameters.setPreviewFrameRate(3);//每秒3帧 // parameters.setPictureFormat(PixelFormat.JPEG);//设置照片的输出格式 // parameters.set("jpeg-quality", 100);//照片质量 // //parameters.setPictureSize(display.getWidth(), display.getHeight());//设置照片的大小 // camera.setParameters(parameters); // camera.setPreviewDisplay(surfaceView.getHolder());//通过SurfaceView显示取景画面 // camera.startPreview();//开始预览 // preview = true; } catch (IOException e) { if (camera != null) { camera.stopPreview(); camera.release(); camera = null; } e.printStackTrace(); } }
下面将整个项目的代码贡献出来,包括实现自动对焦,手动调焦距,设置闪光灯等功能。
共包含一个mainActivity和CameraCallback两个类
mainActivity的代码如下:
import android.app.Activity; import android.hardware.Camera; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.widget.Button; import android.widget.SeekBar; import android.widget.Toast; import android.widget.SeekBar.OnSeekBarChangeListener; public class MainActivity extends Activity implements OnClickListener { private Camera mCamera; private SurfaceView mSurfaceView; private SurfaceHolder mHolder; private CameraCallback mCallback; private Button mTakePicButton; private Button mSwitchButton; private Button mflashButton; private boolean saved = true; private boolean isFrontCamera; public static final int MESSAGE_SVAE_SUCCESS = 0; public static final int MESSAGE_SVAE_FAILURE = 1; private final int FLASH_MODE_AUTO = 0; private final int FLASH_MODE_ON = 1; private final int FLASH_MODE_OFF = 2; private int mFlashMode = 0; private Handler mHandler = new Handler() { public void handleMessage(Message msg) { saved = true; switch (msg.what) { case MESSAGE_SVAE_SUCCESS: Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT) .show(); break; case MESSAGE_SVAE_FAILURE: Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_SHORT) .show(); break; } }; }; private SeekBar mZoomBar; private View mZoomLayout; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initView(); } private void initView() { mTakePicButton = (Button) findViewById(R.id.camera_take_btn); mTakePicButton.setOnClickListener(this); mSwitchButton = (Button) findViewById(R.id.camera_switch_btn); mSwitchButton.setOnClickListener(this); mflashButton = (Button) findViewById(R.id.flashMode); mflashButton.setOnClickListener(this); mZoomLayout = findViewById(R.id.zoomLayout); mZoomBar = (SeekBar) findViewById(R.id.seekBar1); mZoomBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mCallback.setZoom(progress); } }); initSurfaceView(); } private void initSurfaceView() { mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1); mHolder = mSurfaceView.getHolder(); mCallback = new CameraCallback(this); mHolder.addCallback(mCallback); mSurfaceView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP && !isFrontCamera) {// 前置摄像头取消触摸自动聚焦功能 View view = findViewById(R.id.RelativeLayout1); mCallback.autoFocus(view, event); } return true; } }); // 判断是否支持前置摄像头 int cameras = mCallback.getNumberOfCameras(); if (cameras <= 1) { mSwitchButton.setVisibility(View.GONE); } // 是否支持闪关灯 if (!mCallback.isSupportedFlashMode()) { mflashButton.setVisibility(View.GONE); } if (mCallback.isSupportedZoom()) { mZoomBar.setMax(mCallback.getMaxZoom()); } else { mZoomBar.setVisibility(View.GONE); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.camera_take_btn: if (saved) { saved = false; mCallback.takePicture(mHandler); } break; case R.id.camera_switch_btn: isFrontCamera = !isFrontCamera; if (isFrontCamera) { mSwitchButton.setText("打开后摄像头"); mflashButton.setVisibility(View.GONE); mZoomLayout.setVisibility(View.GONE); } else { mSwitchButton.setText("打开前摄像头"); mflashButton.setVisibility(View.VISIBLE); mZoomLayout.setVisibility(View.VISIBLE); } mCallback.switchCamera(mSurfaceView, isFrontCamera); break; case R.id.flashMode: mFlashMode = (mFlashMode + 1) % 3; switch (mFlashMode) { case FLASH_MODE_AUTO: mflashButton.setText("flash_auto"); break; case FLASH_MODE_ON: mflashButton.setText("flash_on"); break; case FLASH_MODE_OFF: mflashButton.setText("flash_off"); break; default: break; } mCallback.SetFlashMode(mFlashMode); break; default: break; } } }
CameraCallback类代码如下:
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.util.List; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.graphics.Bitmap.CompressFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; //import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.Size; import android.os.Environment; import android.os.Handler; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.view.animation.Animation.AnimationListener; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; public class CameraCallback implements SurfaceHolder.Callback { private Context mContext; private Camera mCamera; private boolean isShowFrame; private SurfaceHolder mHolder; // 在2.3的Camera.CameraInfo类中 // CAMERA_FACING_BACK常量的值为0,CAMERA_FACING_FRONT为1 private static final int CAMERA_FACING_BACK = 0; private static final int CAMERA_FACING_FRONT = 1; private final int FLASH_MODE_AUTO = 0; private final int FLASH_MODE_ON = 1; private final int FLASH_MODE_OFF = 2; private Parameters mParameters; public CameraCallback(Context context) { this.mContext = context; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("tag", " surfaceChanged"); mParameters = mCamera.getParameters(); if (isSupportedFlashMode()) {// 需要判断是否支持闪光灯 mParameters.setFlashMode(Parameters.FLASH_MODE_AUTO); } mParameters.setPictureFormat(PixelFormat.JPEG); Log.e("tag", "parameters.getPictureSize()" + mParameters.getPictureSize().width); setPictureSize(mParameters); // parameters.setPreviewFormat(PixelFormat.JPEG);// Log.i("tag", "holder width:" + width + " height:" + height); // parameters.setPreviewSize(width, height);//需要判断支持的预览 mCamera.setParameters(mParameters); mCamera.startPreview(); } @Override public void surfaceCreated(SurfaceHolder holder) { mHolder = holder; Log.e("tag", " surfaceCreated"); try { if (mCamera == null) { mCamera = Camera.open(); } setDisplayOrientation(mCamera); mCamera.setPreviewDisplay(holder); Log.i("", "mCamera 2"); } catch (IOException e) { if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.e("tag", " surfaceDestroyed"); if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } } public int getNumberOfCameras() { try { Method method = Camera.class.getMethod("getNumberOfCameras", null); if (method != null) { Object object = method.invoke(mCamera, null); if (object != null) { return (Integer) object; } } } catch (Exception e) { e.printStackTrace(); } return 0; } private void setDisplayOrientation(Camera camera) { try { Method method = Camera.class.getMethod("setDisplayOrientation", int.class); if (method != null) { method.invoke(camera, 90); } Log.i("tag", "方法名:" + method.getName()); } catch (Exception e) { e.printStackTrace(); } } public Camera open(int i) { try { Method method = Camera.class.getMethod("open", int.class); if (method != null) { Object object = method.invoke(mCamera, i); if (object != null) { return (Camera) object; } } } catch (Exception e) { e.printStackTrace(); } return null; } // 设置图片大小 private void setPictureSize(Parameters parameters) { List<Size> sizes = parameters.getSupportedPictureSizes(); if (sizes == null) { return; } int maxSize = 0; int width = 0; int height = 0; for (int i = 0; i < sizes.size(); i++) { Size size = sizes.get(i); int pix = size.width * size.height; if (pix > maxSize) { maxSize = pix; width = size.width; height = size.height; } } Log.i("tag", "图片的大小:" + width + " height:" + height); parameters.setPictureSize(width, height); } public Camera getCamera() { return mCamera; } // 自动对焦 public void autoFocus(View v, MotionEvent event) { if (isShowFrame) { return; } mCamera.autoFocus(new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { } }); RelativeLayout layout = (RelativeLayout) v; final ImageView imageView = new ImageView(mContext); Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.lcamera_focus_frame1); imageView.setImageBitmap(bitmap); LayoutParams params = new RelativeLayout.LayoutParams( bitmap.getWidth(), bitmap.getHeight()); // imageView.setLayoutParams(params); Log.e("tag", "bitmap.getWidth:" + bitmap.getWidth()); params.leftMargin = (int) (event.getX() - bitmap.getWidth() / 2); params.topMargin = (int) (event.getY() - bitmap.getHeight() / 2); layout.addView(imageView, params); imageView.setVisibility(View.VISIBLE); ScaleAnimation animation = new ScaleAnimation(1, 0.5f, 1, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(300); animation.setFillAfter(true); animation.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(final Animation animation) { imageView.setImageResource(R.drawable.lcamera_focus_frame2); new Thread() { public void run() { try { Thread.sleep(400); ((Activity) (mContext)) .runOnUiThread(new Runnable() { @Override public void run() { imageView .setImageResource(R.drawable.lcamera_focus_frame3); } }); Thread.sleep(200); ((Activity) (mContext)) .runOnUiThread(new Runnable() { @Override public void run() { imageView.clearAnimation(); imageView.setVisibility(View.GONE); isShowFrame = false; } }); } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); } }); imageView.startAnimation(animation); isShowFrame = true; } // 拍照 public void takePicture(final Handler handler) { mCamera.takePicture(null, null, new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { FileOutputStream fos = null; try { File directory; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { directory = new File(Environment .getExternalStorageDirectory(), "camera"); } else { directory = new File(mContext.getCacheDir(), "camera"); } if (!directory.exists()) { directory.mkdir(); } File file = new File(directory, System.currentTimeMillis() + ".jpg"); Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); fos = new FileOutputStream(file); boolean compress = bitmap.compress(CompressFormat.JPEG, 100, fos); if (compress) { handler.sendEmptyMessage(MainActivity.MESSAGE_SVAE_SUCCESS); } else { handler.sendEmptyMessage(MainActivity.MESSAGE_SVAE_FAILURE); } mCamera.startPreview(); Log.i("tag", " 保存是否成功:" + compress + " file.exists:" + file.exists()); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { } } } } }); } // 多镜头切换 public void switchCamera(SurfaceView surfaceView, boolean isFrontCamera) { if (mCamera != null) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } int cameraId = isFrontCamera ? CAMERA_FACING_FRONT : CAMERA_FACING_BACK;// CAMERA_FACING_FRONT为前置摄像头 mCamera = open(cameraId); Parameters parameters = mCamera.getParameters(); Log.e("tag", "parameters.getPictureSize()" + parameters.getPictureSize().width); setPictureSize(parameters); parameters.setPictureFormat(PixelFormat.JPEG); mCamera.setParameters(parameters); Log.e("tag", "2 parameters.getPictureSize()" + parameters.getPictureSize().width); setDisplayOrientation(mCamera); try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } public boolean isSupportedZoom() { if (mCamera != null) { Parameters parameters = mCamera.getParameters(); return parameters.isZoomSupported(); } return false; } public int getMaxZoom() { if (mCamera == null) { mCamera = Camera.open(); } mParameters = mCamera.getParameters(); return mParameters.getMaxZoom(); } // 设置Zoom public void setZoom(int value) { Log.i("tag", "value:" + value); mParameters.setZoom(value); mCamera.setParameters(mParameters); mCamera.startPreview(); } public boolean isSupportedFlashMode() { if (mCamera == null) { mCamera = Camera.open(); } Parameters parameters = mCamera.getParameters(); List<String> modes = parameters.getSupportedFlashModes(); if (modes != null && modes.size() != 0) { boolean autoSupported = modes.contains(Parameters.FLASH_MODE_AUTO); boolean onSupported = modes.contains(Parameters.FLASH_MODE_ON); boolean offSupported = modes.contains(Parameters.FLASH_MODE_OFF); return autoSupported && onSupported && offSupported; } return false; } // 设置闪光灯模式 public void SetFlashMode(int flashMode) { switch (flashMode) { case FLASH_MODE_AUTO: mParameters.setFlashMode(Parameters.FLASH_MODE_AUTO); break; case FLASH_MODE_ON: mParameters.setFlashMode(Parameters.FLASH_MODE_ON); break; case FLASH_MODE_OFF: mParameters.setFlashMode(Parameters.FLASH_MODE_OFF); break; } mCamera.setParameters(mParameters); mCamera.startPreview(); } }
layout中main.xml声明相应的surfaceview和相应的button/seekbar
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/RelativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <SurfaceView android:id="@+id/surfaceView1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginBottom="55dp" /> <RelativeLayout android:id="@+id/camera_bottom" android:layout_width="fill_parent" android:layout_height="55.0dip" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:background="#88ffffff" android:orientation="vertical" > <Button android:id="@+id/camera_take_btn" android:layout_width="60dp" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_gravity="center" android:layout_marginLeft="3.0dip" android:text="拍照" /> </RelativeLayout> <Button android:id="@+id/camera_switch_btn" android:layout_width="120dp" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="5dp" android:gravity="center" android:text="打开前置摄像头" > </Button> <Button android:id="@+id/flashMode" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:layout_marginTop="5dp" android:text="flash_auto" /> <LinearLayout android:id="@+id/zoomLayout" android:layout_width="wrap_content" android:layout_height="20dp" android:layout_above="@+id/camera_bottom" android:layout_centerHorizontal="true" android:layout_marginBottom="5dp" android:orientation="horizontal" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=" - " android:textColor="#ffffff" /> <SeekBar android:id="@+id/seekBar1" android:layout_width="250dp" android:layout_height="13dp" android:layout_gravity="center_vertical" android:maxHeight="6dp" android:progressDrawable="@drawable/seekbar_progress" android:thumb="@drawable/camera_seekbar_progress_ball" android:thumbOffset="0dp" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="top" android:gravity="top" android:text=" + " android:textColor="#ffffff" /> </LinearLayout> </RelativeLayout>
在AndroidManifest中做如下权限声明
<uses-sdk android:minSdkVersion="8" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" />
posted on 2013-05-12 16:47 chlde2500 阅读(6080) 评论(2) 编辑 收藏 举报