这是一个用surfaceview来捕捉摄像头画面并拍照存储图片到sdcard的demo。众所周知,在一个应用中,我们可以通过intent来调用系统自带的相机功能进行拍
照,但,这样做不如自己写一个拍照界面来的酷!用surfaceview的方式来做,你可以随心所欲的设计自己的界面。
在这个例子中,我用代码制作了一个拍摄界面,里面只有三个控件,一个是自己封装的CameraView,它继承了SurfaceView,一个是悬浮在CameraView上的按
钮,点击它可以捕捉画面并把图像存储到sdCard的根目录下,还有一个是悬浮在CameraView上的TextView,它不过显示一行文字而已。
程序运行截图如下:
存储在sdcard根目录下的图片如下:
你可以看到,在surfaceview中所呈现的图像和保存的图像是一样的,这样做保证了所见即所得。
代码如下:
这是MainActivity,程序一运行就打开的Activity:
public class MainActivity extends Activity { private CameraView mCameraView; private Button takePictureBtn; private Camera mCamera; private Bitmap mBitmap; private int bitmapWidth; private int bitmapHeight; private RelativeLayout rl; // 准备一个保存图片的pictureCallback对象 public Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub if(camera != null){ Toast.makeText(getApplicationContext(), "正在保存...", Toast.LENGTH_LONG).show(); // 用BitmapFactory.decodeByteArray()方法可以把相机传回的裸数据转换成Bitmap对象 // 这里通过BitmapFactory.Options类指定解码方法 BitmapFactory.Options options = new BitmapFactory.Options(); // 在解码图片的时候设置inJustDecodeBounds属性为true,可以避免内存分配 // options.inJustDecodeBounds = true; 这句话已开启就会死机 mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options); bitmapWidth = options.outWidth; bitmapHeight = options.outHeight; // 把bitmap保存成一个存储卡中的文件 File file = new File("/sdcard/YY"+ new DateFormat().format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".png"); try { file.createNewFile(); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(file)); mBitmap.compress(Bitmap.CompressFormat.PNG, 100, os); os.flush(); os.close(); Toast.makeText(getApplicationContext(), "图片 " + bitmapWidth + "X" + bitmapHeight +" 保存完毕,在存储卡的根目录", Toast.LENGTH_LONG).show(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //窗口去掉标题 requestWindowFeature(Window.FEATURE_NO_TITLE); //窗口设置为全屏 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); //设置窗口为半透明 getWindow().setFormat(PixelFormat.TRANSLUCENT); // 创建布局 rl = new RelativeLayout(this); // 添加CameraView addCameraView(rl); // 添加拍摄按钮 addTakePictureBtn(rl); // 添加提示文字 addTextView(rl); // 设置布局 setContentView(rl); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } /** * 添加CamearView * @param rl 所在的布局 */ private void addCameraView(RelativeLayout rl){ // 打开相机 this.mCamera = Camera.open(); // 设置CameraView的大小和位置 // 1、首先得到保存在sdcard的图片大小 Camera.Parameters parameters = this.mCamera.getParameters(); Size sdCardPictureSize = CameraView.getOptimalPictureSize(parameters.getSupportedPictureSizes(), 640, 480); // 2、得到设备的屏幕分辨率 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); // 3、根据以上两个数据计算出CameraView的大小 float scale = (float)dm.heightPixels/(float)sdCardPictureSize.height; int cameraViewWidth = (int) (sdCardPictureSize.width * scale); int cameraViewHeight = (int) (sdCardPictureSize.height * scale); System.out.println("scale: " + scale); System.out.println("cameraView: " + cameraViewWidth + ", " + cameraViewHeight); // 4、把CameraView居中布局 RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(cameraViewWidth, cameraViewHeight); rlp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE); // 5、把cameraView添加到rl上 this.mCameraView = new CameraView(this); this.mCameraView.setCamera(mCamera); rl.addView(this.mCameraView, rlp); } /** * 添加拍摄按钮 * @param rl */ private void addTakePictureBtn(RelativeLayout rl){ this.takePictureBtn = new Button(this); this.takePictureBtn.setText("拍摄"); this.takePictureBtn.setOnClickListener(new TakePictureBtnOnClickListener()); // 把按钮放置在屏幕右下角 RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); rlp.rightMargin = 15; rlp.bottomMargin = 15; rl.addView(takePictureBtn, rlp); } /** * 添加提示文字 * @param rl */ private void addTextView(RelativeLayout rl){ TextView textView = new TextView(this); textView.setText("请点击拍摄按钮拍摄:"); // 把文字放在屏幕左上角 RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT); rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); rlp.addRule(RelativeLayout.ALIGN_TOP); rl.addView(textView, rlp); } /** * 拍摄按钮的OnClickListener,在这里执行拍照。 * @author haozi * */ class TakePictureBtnOnClickListener implements View.OnClickListener{ @Override public void onClick(View v) { if(mCamera != null){ mCamera.takePicture(null, null, mPictureCallback); }else{ Toast.makeText(getApplicationContext(), "Camera对象为空!", Toast.LENGTH_LONG).show(); } } } }
/** * 摄像的View * @author haozi * */ public class CameraView extends SurfaceView { public Context mContext; private SurfaceHolder mSurfaceHolder; public CameraView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; } public CameraView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.mContext = context; } public CameraView(Context context) { super(context); this.mContext = context; } /** * 把照相机对象传入 * @param mCamera */ public void setCamera(Camera mCamera){ // 操作surface的holder mSurfaceHolder = this.getHolder(); // 创建surfaceholder对象 mSurfaceHolder.addCallback(new SurfaceHolderCallback(mCamera)); // 设置push缓冲类型,说明surface数据由其他来源提供。而不是用自己的Canvas来绘图,在这里由摄像头来提供数据。 mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } /** * 得到最适合的预览大小 * @param sizes * @param w * @param h * @return */ public static Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } /** * 得到最合适的PictureSize * @param sizes * @param w * @param h * @return */ public static Size getOptimalPictureSize(List<Size> sizes, int w, int h) { final double ASPECT_TOLERANCE = 0.1; double targetRatio = (double) w / h; if (sizes == null) return null; Size optimalSize = null; double minDiff = Double.MAX_VALUE; int targetHeight = h; // Try to find an size match aspect ratio and size for (Size size : sizes) { double ratio = (double) size.width / size.height; if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } // Cannot find the one match the aspect ratio, ignore the requirement if (optimalSize == null) { minDiff = Double.MAX_VALUE; for (Size size : sizes) { if (Math.abs(size.height - targetHeight) < minDiff) { optimalSize = size; minDiff = Math.abs(size.height - targetHeight); } } } return optimalSize; } /** * 摄像头捕捉到的画面都会在这里被处理 * @author haozi * */ class SurfaceHolderCallback implements SurfaceHolder.Callback{ private Camera mCamera; public SurfaceHolderCallback(Camera mCamera){ this.mCamera = mCamera; } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub // 停止预览 mCamera.stopPreview(); // 释放相机资源并置空 mCamera.release(); mCamera = null; } @Override public void surfaceCreated(SurfaceHolder holder) { // 设置预览 try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); // 如果出现异常,释放相机资源并置空 mCamera.release(); mCamera = null; } } // 当surface视图数据发生变化时,处理预览信息 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub // 如果相机资源并不为空 if(mCamera != null){ // 获得相机参数对象 Camera.Parameters parameters = mCamera.getParameters(); // 获取最合适的参数,为了做到拍摄的时候所见即所得,我让previewSize和pictureSize相等 Size previewSize = getOptimalPictureSize(parameters.getSupportedPictureSizes(), 640, 480); Size pictureSize = getOptimalPictureSize(parameters.getSupportedPictureSizes(), 640, 480); System.out.println("---------------------------------------------------------------"); System.out.println("previewSize: " + previewSize.width + ", " + previewSize.height); System.out.println("pictureSize: " + pictureSize.width + ", " + pictureSize.height); // 设置照片格式 parameters.setPictureFormat(PixelFormat.JPEG); // 设置预览大小 parameters.setPreviewSize(previewSize.width, previewSize.height); // 设置自动对焦,先进行判断 List<String> focusModes = parameters.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO); } // 设置图片保存时候的分辨率大小 parameters.setPictureSize(pictureSize.width, pictureSize.height); // 给相机对象设置刚才设置的参数 mCamera.setParameters(parameters); // 开始预览 mCamera.startPreview(); } } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.haozi.demo.screenshot4" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="10" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.haozi.demo.screenshot4.MainActivity" android:label="@string/app_name" android:screenOrientation="landscape" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.FLASHLIGHT"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.autofocus"/> </manifest>
源码下载地址:http://download.csdn.net/detail/hello_haozi/5014830