开源分享二(Android相机开发实战)

开源分享 一(StickerCamera + 仿微信多图选择)

开源分享三(炫酷的Android Loading动画)


前言


上篇博文给大家分享了两个非常实用的项目功能模块,不知道大伙感觉如何?有木有一种臭袜子味扑鼻,酸爽的赶脚!!!贱笑贱笑了~ ~

OK!不扯淡了,言归正传。本文将主要为大家介绍Android中自定义相机的开发,做Android应用的童鞋应该都知道,在应用中使用相机功能有两种方式:

  • 调用Camera API 自定义相机
  • 调用系统相机

由于需求不同,所以选择的方案固然也不同,至于第二种调用系统相机,这里就不过多讲解了,使用Intent对象设置一个Action动作即可,跳转时使用startActivityForResult,然后在onActivityResult处理相关数据便可,关键代码:

intent.setAction("android.media.action.STILL_IMAGE_CAMERA");

至于使用,较常见的一般是应用中用户上传头像的时候调用,然后返回处理图像数据。

而第一种自定义相机的方式使用也十分普遍,但是要做好这个模块,相对来说还是有一定难度的,之前分享过一个Github上的开源相机的项目,项目由美国的一个团队开发,集 拍照、摄影、各种特效动画 等功能与一身,本人之前研究了下,发现功能比较全面也很强大,抠出来单独拍照那一个模块,我滴妈呀,真TM费劲!相机不管是预览还是拍摄图像都还是很清晰的,自己当时也写了一个,比较操蛋,只能怪自己对这一块的优化了解浅显吧!特别是预览的时候,聚焦完成后,焦点周边会出现很多白色的噪点,密密麻麻,特别严重,头疼的很。不过也总算解决了,灰常感谢USA的那个什么什么团队的开源相机程序。经过自己改造后的预览效果图:

下面看下这个项目的效果图,我也把地址甩底下,大伙感兴趣的自行Clone研究(或者闲的蛋疼也可以抽时间剥离开每一个模块学习,作为日后的知识储备),里面也用到了这个Android中读取图片EXIF元数据之metadata-extractor的使用

GitHub:https://github.com/xplodwild/android_packages_apps_Focal


相机开发简介

下面说说在Android中调用Camera来定义相机的最基本步骤:

  1. 打开相机 —— 调用Camera的open()方法。
  2. 获取拍照参数 —— 调用Camera的getParameters()方法,返回Camera.Parameters对象。
  3. 拍照参数设置 —— 调用Camera.Parameters对象。
  4. 拍照参数控制 —— 调用Camera的setParameters(),并将Camera.Parameters对象作为参数传入。注:Android2.3.3之后不用设置。
  5. 预览取景 —— 调用Camera的startPreview()方法,在之前注意调用Camera的setPreviewDisplay(SurfaceHolder holder)设置使用哪个SurfaceView来显示取得的图片。
  6. 拍照 —— 调用Camera的takePicture()
  7. 停止预览 —— 调用Camera的stopPreview()方法
  8. 资源释放 —— Camera.release()

开启和关闭预览的联系如下:Camera ---- SurfaceHolder ------ SurfaceView

关于SurfaceHolder.Callback必须实现的3个方法:

surfaceCreated() 该方法在surfaceView被Create时调用
surfaceChanged() 该方法是当surfaceView发生改变后调用
surfaceDestroyed() 这个不用说了,销毁时调用

surfaceHolder通过addCallBack()方法将响应的接口绑定

注:必要Camera权限,例如:

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.CAMERA"/><uses-feature android:name="android.hardware.camera" /><uses-permission android:name="android.hardware.camera.autofocus" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

关于Camera下的Parameters类,其中封装了我们需要的大部分功能,下面做个简单介绍:

  1. setPictureFormat() 方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,如:PixelFormat.JPEG。
  2. setSceneMode() 方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。
  3. setZoom() 方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。
  4. setPictureSize() 方法用于设置相机照片的大小,参数为整型。
  5. setWhiteBalance() 方法用于设置相机照片白平衡,其参数是一个字符型,位于Parameters类中,以WHITE_BALANCE开头。
  6. setJpegQuality() 方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。
  7. setFlashMode() 方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。
  8. setColorEffect() 方法用于设置照片颜色特效的类型,其参数是一个字符型参数,位于Parameters类中,以EFFECT_开头。

本程序模块效果图及示例

下面分享本篇Blog的示例相机模块,此功能模块并非上面开源项目中的剥离出来的,看下效果图咯:

         

         

效果看着还可以吧(不点赞也太不给面子了吧  - . - ),下面个出主界面的布局代码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/layout"android:layout_width="match_parent"android:layout_height="match_parent" ><!-- 预览画布 --><SurfaceViewandroid:id="@+id/surfaceView"android:layout_width="match_parent"android:layout_height="match_parent" /><!-- 闪光灯、前置摄像头、后置摄像头、聚焦 --><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent" ><org.gaochun.camera.CameraGridandroid:id="@+id/camera_grid"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_alignParentTop="true" /><Viewandroid:id="@+id/focus_index"android:layout_width="40dp"android:layout_height="40dp"android:background="@drawable/camera_focus"android:visibility="invisible" /><ImageViewandroid:id="@+id/flash_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:onClick="onClick"android:padding="15dp"android:scaleType="centerCrop"android:src="@drawable/camera_flash_off" /><ImageViewandroid:id="@+id/camera_flip_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:onClick="onClick"android:padding="15dp"android:scaleType="centerCrop"android:src="@drawable/camera_flip" /><!-- 底部按钮 --><RelativeLayoutandroid:layout_width="fill_parent"android:layout_height="70dp"android:layout_alignParentBottom="true"android:background="#a0000000"android:padding="5dp" ><Buttonandroid:id="@+id/search"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:background="@null"android:drawablePadding="3dp"android:drawableTop="@drawable/ic_search_selector"android:onClick="onClick"android:text="搜图"android:textColor="@drawable/row_selector_text" /><ImageViewandroid:id="@+id/action_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:clickable="true"android:onClick="onClick"android:src="@drawable/btn_shutter_photo" /><Buttonandroid:id="@+id/takephoto"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_marginRight="30dp"android:background="@null"android:drawablePadding="3dp"android:drawableTop="@drawable/ic_takephoto_selector"android:onClick="onClick"android:text="拍照"android:textColor="@drawable/row_selector_text" /></RelativeLayout></RelativeLayout></FrameLayout>

下面是核心模块 CameraPreview 类:

public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback, Camera.AutoFocusCallback {private SurfaceView mSurfaceView;private SurfaceHolder mHolder;private Size mPreviewSize;private Size adapterSize;//private List<Size> mSupportedPreviewSizes;private Camera mCamera;private boolean isSupportAutoFocus = false;private Camera.Parameters parameters = null;private Context mContext;//private int mCurrentCameraId = 0;private int screenWidth;private int screenHeight;CameraPreview(Context context, SurfaceView sv) {super(context);mContext = context;mSurfaceView = sv;mHolder = mSurfaceView.getHolder();mHolder.addCallback(this);mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);mHolder.setKeepScreenOn(true);isSupportAutoFocus = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS);DisplayMetrics dm = new DisplayMetrics();((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(dm);screenWidth = dm.widthPixels;screenHeight = dm.heightPixels;}public void setCamera(Camera camera) {mCamera = camera;initCamera();}public void initCamera() {if (mCamera != null) {Camera.Parameters params = mCamera.getParameters();//mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();requestLayout();if (mPreviewSize == null) {mPreviewSize = findBestPreviewResolution();}if (adapterSize == null) {adapterSize = findBestPictureResolution();}if (adapterSize != null) {params.setPictureSize(adapterSize.width, adapterSize.height);}if (mPreviewSize != null) {params.setPreviewSize(mPreviewSize.width, mPreviewSize.height);}params.setPictureFormat(PixelFormat.JPEG);List<String> focusModes = params.getSupportedFocusModes();if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {// set the focus modeparams.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);// set Camera parametersmCamera.setParameters(params);}setDispaly(params, mCamera);//setCameraDisplayOrientation((Activity) mContext, mCurrentCameraId, mCamera);mCamera.setParameters(params);}}//控制图像的正确显示方向private void setDispaly(Camera.Parameters parameters, Camera camera) {if (Build.VERSION.SDK_INT >= 8) {setDisplayOrientation(camera, 90);} else {parameters.setRotation(90);}}//实现的图像的正确显示private void setDisplayOrientation(Camera camera, int i) {Method downPolymorphic;try {downPolymorphic = camera.getClass().getMethod("setDisplayOrientation",new Class[]{int.class});if (downPolymorphic != null) {downPolymorphic.invoke(camera, new Object[]{i});}} catch (Exception e) {e.printStackTrace();}}public static void setCameraDisplayOrientation(Activity activity,int cameraId, android.hardware.Camera camera) {android.hardware.Camera.CameraInfo info =new android.hardware.Camera.CameraInfo();android.hardware.Camera.getCameraInfo(cameraId, info);int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();int degrees = 0;switch (rotation) {case Surface.ROTATION_0:degrees = 0;break;case Surface.ROTATION_90:degrees = 90;break;case Surface.ROTATION_180:degrees = 180;break;case Surface.ROTATION_270:degrees = 270;break;}int result;if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {result = (info.orientation + degrees) % 360;result = (360 - result) % 360;  // compensate the mirror} else {  // back-facingresult = (info.orientation - degrees + 360) % 360;}camera.setDisplayOrientation(result);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);setMeasuredDimension(width, height);//        if (mSupportedPreviewSizes != null) {//             mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);//        }}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (changed && getChildCount() > 0) {final View child = getChildAt(0);final int width = r - l;final int height = b - t;int previewWidth = width;int previewHeight = height;if (mPreviewSize != null) {previewWidth = mPreviewSize.width;previewHeight = mPreviewSize.height;}// Center the child SurfaceView within the parent.if (width * previewHeight > height * previewWidth) {final int scaledChildWidth = previewWidth * height / previewHeight;child.layout((width - scaledChildWidth) / 2, 0,(width + scaledChildWidth) / 2, height);} else {final int scaledChildHeight = previewHeight * width / previewWidth;child.layout(0, (height - scaledChildHeight) / 2,width, (height + scaledChildHeight) / 2);}}}public void surfaceCreated(SurfaceHolder holder) {// The Surface has been created, acquire the camera and tell it where// to draw.try {if (mCamera != null) {mCamera.setPreviewDisplay(holder);}} catch (IOException e) {if (null != mCamera) {mCamera.release();mCamera = null;}e.printStackTrace();}}public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {if (holder.getSurface() == null) {return;}if (mCamera != null) {Camera.Parameters parameters = mCamera.getParameters();parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);mCamera.setParameters(parameters);try {mCamera.setPreviewDisplay(holder);} catch (IOException e) {e.printStackTrace();}mCamera.startPreview();reAutoFocus();}}public void surfaceDestroyed(SurfaceHolder holder) {// Surface will be destroyed when we return, so stop the preview.if (mCamera != null) {mCamera.stopPreview();}}/*** 最小预览界面的分辨率*/private static final int MIN_PREVIEW_PIXELS = 480 * 320;/*** 最大宽高比差*/private static final double MAX_ASPECT_DISTORTION = 0.15;/*** 找出最适合的预览界面分辨率** @return*/private Camera.Size findBestPreviewResolution() {Camera.Parameters cameraParameters = mCamera.getParameters();Camera.Size defaultPreviewResolution = cameraParameters.getPreviewSize();List<Camera.Size> rawSupportedSizes = cameraParameters.getSupportedPreviewSizes();if (rawSupportedSizes == null) {return defaultPreviewResolution;}// 按照分辨率从大到小排序List<Camera.Size> supportedPreviewResolutions = new ArrayList<Camera.Size>(rawSupportedSizes);Collections.sort(supportedPreviewResolutions, new Comparator<Size>() {@Overridepublic int compare(Camera.Size a, Camera.Size b) {int aPixels = a.height * a.width;int bPixels = b.height * b.width;if (bPixels < aPixels) {return -1;}if (bPixels > aPixels) {return 1;}return 0;}});StringBuilder previewResolutionSb = new StringBuilder();for (Camera.Size supportedPreviewResolution : supportedPreviewResolutions) {previewResolutionSb.append(supportedPreviewResolution.width).append('x').append(supportedPreviewResolution.height).append(' ');}// 移除不符合条件的分辨率double screenAspectRatio = (double) screenWidth/ screenHeight;Iterator<Size> it = supportedPreviewResolutions.iterator();while (it.hasNext()) {Camera.Size supportedPreviewResolution = it.next();int width = supportedPreviewResolution.width;int height = supportedPreviewResolution.height;// 移除低于下限的分辨率,尽可能取高分辨率if (width * height < MIN_PREVIEW_PIXELS) {it.remove();continue;}// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height// 因此这里要先交换然preview宽高比后在比较boolean isCandidatePortrait = width > height;int maybeFlippedWidth = isCandidatePortrait ? height : width;int maybeFlippedHeight = isCandidatePortrait ? width : height;double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;double distortion = Math.abs(aspectRatio - screenAspectRatio);if (distortion > MAX_ASPECT_DISTORTION) {it.remove();continue;}// 找到与屏幕分辨率完全匹配的预览界面分辨率直接返回if (maybeFlippedWidth == screenWidth&& maybeFlippedHeight == screenHeight) {return supportedPreviewResolution;}}// 如果没有找到合适的,并且还有候选的像素,则设置其中最大比例的,对于配置比较低的机器不太合适if (!supportedPreviewResolutions.isEmpty()) {Camera.Size largestPreview = supportedPreviewResolutions.get(0);return largestPreview;}// 没有找到合适的,就返回默认的return defaultPreviewResolution;}private Camera.Size findBestPictureResolution() {Camera.Parameters cameraParameters = mCamera.getParameters();List<Camera.Size> supportedPicResolutions = cameraParameters.getSupportedPictureSizes(); // 至少会返回一个值StringBuilder picResolutionSb = new StringBuilder();for (Camera.Size supportedPicResolution : supportedPicResolutions) {picResolutionSb.append(supportedPicResolution.width).append('x').append(supportedPicResolution.height).append(" ");}Camera.Size defaultPictureResolution = cameraParameters.getPictureSize();// 排序List<Camera.Size> sortedSupportedPicResolutions = new ArrayList<Camera.Size>(supportedPicResolutions);Collections.sort(sortedSupportedPicResolutions, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size a, Camera.Size b) {int aPixels = a.height * a.width;int bPixels = b.height * b.width;if (bPixels < aPixels) {return -1;}if (bPixels > aPixels) {return 1;}return 0;}});// 移除不符合条件的分辨率double screenAspectRatio = screenWidth/ (double) screenHeight;Iterator<Camera.Size> it = sortedSupportedPicResolutions.iterator();while (it.hasNext()) {Camera.Size supportedPreviewResolution = it.next();int width = supportedPreviewResolution.width;int height = supportedPreviewResolution.height;// 在camera分辨率与屏幕分辨率宽高比不相等的情况下,找出差距最小的一组分辨率// 由于camera的分辨率是width>height,我们设置的portrait模式中,width<height// 因此这里要先交换然后在比较宽高比boolean isCandidatePortrait = width > height;int maybeFlippedWidth = isCandidatePortrait ? height : width;int maybeFlippedHeight = isCandidatePortrait ? width : height;double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;double distortion = Math.abs(aspectRatio - screenAspectRatio);if (distortion > MAX_ASPECT_DISTORTION) {it.remove();continue;}}// 如果没有找到合适的,并且还有候选的像素,对于照片,则取其中最大比例的,而不是选择与屏幕分辨率相同的if (!sortedSupportedPicResolutions.isEmpty()) {return sortedSupportedPicResolutions.get(0);}// 没有找到合适的,就返回默认的return defaultPictureResolution;}private 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 sizefor (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 requirementif (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;}public void reAutoFocus() {if (isSupportAutoFocus) {mCamera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {}});}}public List<Size> getResolutionList() {return mCamera.getParameters().getSupportedPreviewSizes();}public Camera.Size getResolution() {Camera.Parameters params = mCamera.getParameters();Camera.Size s = params.getPreviewSize();return s;}/*public void setCurrentCameraId(int current) {mCurrentCameraId = current;}*///定点对焦的代码public void pointFocus(MotionEvent event) {mCamera.cancelAutoFocus();parameters = mCamera.getParameters();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {//showPoint(x, y);focusOnTouch(event);}mCamera.setParameters(parameters);autoFocus();}//实现自动对焦public void autoFocus() {new Thread() {@Overridepublic void run() {try {sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (mCamera == null) {return;}mCamera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {if (success) {initCamera();//实现相机的参数初始化}}});}};}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)private void showPoint(int x, int y) {if (parameters.getMaxNumMeteringAreas() > 0) {List<Camera.Area> areas = new ArrayList<Camera.Area>();WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);//xy变换了int rectY = -x * 2000 / wm.getDefaultDisplay().getWidth() + 1000;int rectX = y * 2000 / wm.getDefaultDisplay().getHeight() - 1000;int left = rectX < -900 ? -1000 : rectX - 100;int top = rectY < -900 ? -1000 : rectY - 100;int right = rectX > 900 ? 1000 : rectX + 100;int bottom = rectY > 900 ? 1000 : rectY + 100;Rect area1 = new Rect(left, top, right, bottom);areas.add(new Camera.Area(area1, 800));parameters.setMeteringAreas(areas);}parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);}@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)public void focusOnTouch(MotionEvent event) {Rect focusRect = calculateTapArea(event.getRawX(), event.getRawY(), 1f);Rect meteringRect = calculateTapArea(event.getRawX(), event.getRawY(), 1.5f);Camera.Parameters parameters = mCamera.getParameters();parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);if (parameters.getMaxNumFocusAreas() > 0) {List<Camera.Area> focusAreas = new ArrayList<Camera.Area>();focusAreas.add(new Camera.Area(focusRect, 1000));parameters.setFocusAreas(focusAreas);}if (parameters.getMaxNumMeteringAreas() > 0) {List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();meteringAreas.add(new Camera.Area(meteringRect, 1000));parameters.setMeteringAreas(meteringAreas);}mCamera.setParameters(parameters);mCamera.autoFocus(this);}/*** Convert touch position x:y to {@link Camera.Area} position -1000:-1000 to 1000:1000.*/private Rect calculateTapArea(float x, float y, float coefficient) {float focusAreaSize = 300;int areaSize = Float.valueOf(focusAreaSize * coefficient).intValue();int centerX = (int) (x / getResolution().width * 2000 - 1000);int centerY = (int) (y / getResolution().height * 2000 - 1000);int left = clamp(centerX - areaSize / 2, -1000, 1000);int right = clamp(left + areaSize, -1000, 1000);int top = clamp(centerY - areaSize / 2, -1000, 1000);int bottom = clamp(top + areaSize, -1000, 1000);return new Rect(left, top, right, bottom);}private int clamp(int x, int min, int max) {if (x > max) {return max;}if (x < min) {return min;}return x;}@Overridepublic void onAutoFocus(boolean success, Camera camera) {}public void setNull() {adapterSize = null;mPreviewSize = null;}}

以下是CameraActivity类:

public class CameraActivity extends Activity implements View.OnTouchListener,OnClickListener {public static final String CAMERA_PATH_VALUE1 = "PHOTO_PATH";public static final String CAMERA_PATH_VALUE2 = "PATH";public static final String CAMERA_TYPE = "CAMERA_TYPE";public static final String CAMERA_RETURN_PATH = "return_path";private int PHOTO_SIZE_W = 2000;private int PHOTO_SIZE_H = 2000;public static final int CAMERA_TYPE_1 = 1;public static final int CAMERA_TYPE_2 = 2;private final int PROCESS = 1;private CameraPreview preview;private Camera camera;private Context mContext;private View focusIndex;private ImageView flashBtn;private int mCurrentCameraId = 0; // 1是前置 0是后置private SurfaceView mSurfaceView;private CameraGrid mCameraGrid;private int type = 1;  //引用的矩形框private Button mBtnSearch;private Button mBtnTakePhoto;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mContext = this;//requestWindowFeature(Window.FEATURE_NO_TITLE);//getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//拍照过程屏幕一直处于高亮setContentView(R.layout.camera_home);type = getIntent().getIntExtra(CAMERA_TYPE, CAMERA_TYPE_2);initView();InitData();}private void initView() {focusIndex = (View) findViewById(R.id.focus_index);flashBtn = (ImageView) findViewById(R.id.flash_view);mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);mCameraGrid = (CameraGrid) findViewById(R.id.camera_grid);mBtnSearch = (Button) findViewById(R.id.search);mBtnTakePhoto = (Button) findViewById(R.id.takephoto);}private void InitData() {preview = new CameraPreview(this, mSurfaceView);preview.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));((FrameLayout) findViewById(R.id.layout)).addView(preview);preview.setKeepScreenOn(true);mSurfaceView.setOnTouchListener(this);mCameraGrid.setType(type);}private Handler handler = new Handler();private void takePhoto() {try {camera.takePicture(shutterCallback, rawCallback, jpegCallback);} catch (Throwable t) {t.printStackTrace();Toast.makeText(getApplication(), "拍照失败,请重试!", Toast.LENGTH_LONG).show();try {camera.startPreview();} catch (Throwable e) {}}}@Overrideprotected void onResume() {super.onResume();int numCams = Camera.getNumberOfCameras();if (numCams > 0) {try {mCurrentCameraId = 0;camera = Camera.open(mCurrentCameraId);camera.startPreview();preview.setCamera(camera);preview.reAutoFocus();} catch (RuntimeException ex) {Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();}}}@Overrideprotected void onPause() {if (camera != null) {camera.stopPreview();preview.setCamera(null);camera.release();camera = null;preview.setNull();}super.onPause();}private void resetCam() {camera.startPreview();preview.setCamera(camera);}ShutterCallback shutterCallback = new ShutterCallback() {public void onShutter() {}};PictureCallback rawCallback = new PictureCallback() {public void onPictureTaken(byte[] data, Camera camera) {}};PictureCallback jpegCallback = new PictureCallback() {public void onPictureTaken(byte[] data, Camera camera) {new SaveImageTask(data).execute();resetCam();}};@Overridepublic boolean onTouch(View v, MotionEvent event) {try {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {preview.pointFocus(event);}} catch (Exception e) {e.printStackTrace();}RelativeLayout.LayoutParams layout = new RelativeLayout.LayoutParams(focusIndex.getLayoutParams());layout.setMargins((int) event.getX() - 60, (int) event.getY() - 60, 0,0);focusIndex.setLayoutParams(layout);focusIndex.setVisibility(View.VISIBLE);ScaleAnimation sa = new ScaleAnimation(3f, 1f, 3f, 1f,ScaleAnimation.RELATIVE_TO_SELF, 0.5f,ScaleAnimation.RELATIVE_TO_SELF, 0.5f);sa.setDuration(800);focusIndex.startAnimation(sa);handler.postAtTime(new Runnable() {@Overridepublic void run() {focusIndex.setVisibility(View.INVISIBLE);}}, 800);return false;}@Overridepublic void onClick(View v) {switch (v.getId()) {/*case R.id.camera_back:setResult(0);finish();break;*/case R.id.camera_flip_view:switchCamera();break;case R.id.flash_view:turnLight(camera);break;case R.id.action_button:takePhoto();break;case R.id.search: //处理选中状态mBtnSearch.setSelected(true);mBtnTakePhoto.setSelected(false);break;case R.id.takephoto:    //处理选中状态mBtnTakePhoto.setSelected(true);mBtnSearch.setSelected(false);break;}}private static String getCameraPath() {Calendar calendar = Calendar.getInstance();StringBuilder sb = new StringBuilder();sb.append("IMG");sb.append(calendar.get(Calendar.YEAR));int month = calendar.get(Calendar.MONTH) + 1; // 0~11sb.append(month < 10 ? "0" + month : month);int day = calendar.get(Calendar.DATE);sb.append(day < 10 ? "0" + day : day);int hour = calendar.get(Calendar.HOUR_OF_DAY);sb.append(hour < 10 ? "0" + hour : hour);int minute = calendar.get(Calendar.MINUTE);sb.append(minute < 10 ? "0" + minute : minute);int second = calendar.get(Calendar.SECOND);sb.append(second < 10 ? "0" + second : second);if (!new File(sb.toString() + ".jpg").exists()) {return sb.toString() + ".jpg";}StringBuilder tmpSb = new StringBuilder(sb);int indexStart = sb.length();for (int i = 1; i < Integer.MAX_VALUE; i++) {tmpSb.append('(');tmpSb.append(i);tmpSb.append(')');tmpSb.append(".jpg");if (!new File(tmpSb.toString()).exists()) {break;}tmpSb.delete(indexStart, tmpSb.length());}return tmpSb.toString();}//处理拍摄的照片private class SaveImageTask extends AsyncTask<Void, Void, String> {private byte[] data;SaveImageTask(byte[] data) {this.data = data;}@Overrideprotected String doInBackground(Void... params) {// Write to SD CardString path = "";try {showProgressDialog("处理中");path = saveToSDCard(data);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {}return path;}@Overrideprotected void onPostExecute(String path) {super.onPostExecute(path);if (!TextUtils.isEmpty(path)) {Log.d("DemoLog", "path=" + path);dismissProgressDialog();Intent intent = new Intent();intent.setClass(CameraActivity.this, PhotoProcessActivity.class);intent.putExtra(CAMERA_PATH_VALUE1, path);startActivityForResult(intent, PROCESS);} else {Toast.makeText(getApplication(), "拍照失败,请稍后重试!",Toast.LENGTH_LONG).show();}}}private AlertDialog mAlertDialog;private void dismissProgressDialog() {this.runOnUiThread(new Runnable() {@Overridepublic void run() {if (mAlertDialog != null && mAlertDialog.isShowing()&& !CameraActivity.this.isFinishing()) {mAlertDialog.dismiss();mAlertDialog = null;}}});}private void showProgressDialog(final String msg) {this.runOnUiThread(new Runnable() {@Overridepublic void run() {if (mAlertDialog == null) {mAlertDialog = new GenericProgressDialog(CameraActivity.this);}mAlertDialog.setMessage(msg);((GenericProgressDialog) mAlertDialog).setProgressVisiable(true);mAlertDialog.setCancelable(false);mAlertDialog.setOnCancelListener(null);mAlertDialog.show();mAlertDialog.setCanceledOnTouchOutside(false);}});}/*** 将拍下来的照片存放在SD卡中*/public String saveToSDCard(byte[] data) throws IOException {Bitmap croppedImage;// 获得图片大小BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeByteArray(data, 0, data.length, options);// PHOTO_SIZE = options.outHeight > options.outWidth ? options.outWidth// : options.outHeight;PHOTO_SIZE_W = options.outWidth;PHOTO_SIZE_H = options.outHeight;options.inJustDecodeBounds = false;Rect r = new Rect(0, 0, PHOTO_SIZE_W, PHOTO_SIZE_H);try {croppedImage = decodeRegionCrop(data, r);} catch (Exception e) {return null;}String imagePath = "";try {imagePath = saveToFile(croppedImage);} catch (Exception e) {}croppedImage.recycle();return imagePath;}private Bitmap decodeRegionCrop(byte[] data, Rect rect) {InputStream is = null;System.gc();Bitmap croppedImage = null;try {is = new ByteArrayInputStream(data);BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is,false);try {croppedImage = decoder.decodeRegion(rect,new BitmapFactory.Options());} catch (IllegalArgumentException e) {}} catch (Throwable e) {e.printStackTrace();} finally {}Matrix m = new Matrix();m.setRotate(90, PHOTO_SIZE_W / 2, PHOTO_SIZE_H / 2);if (mCurrentCameraId == 1) {m.postScale(1, -1);}Bitmap rotatedImage = Bitmap.createBitmap(croppedImage, 0, 0,PHOTO_SIZE_W, PHOTO_SIZE_H, m, true);if (rotatedImage != croppedImage)croppedImage.recycle();return rotatedImage;}// 保存图片文件public static String saveToFile(Bitmap croppedImage)throws FileNotFoundException, IOException {File sdCard = Environment.getExternalStorageDirectory();File dir = new File(sdCard.getAbsolutePath() + "/DCIM/Camera/");if (!dir.exists()) {dir.mkdirs();}String fileName = getCameraPath();File outFile = new File(dir, fileName);FileOutputStream outputStream = new FileOutputStream(outFile); // 文件输出流croppedImage.compress(Bitmap.CompressFormat.JPEG, 70, outputStream);outputStream.flush();outputStream.close();return outFile.getAbsolutePath();}/*** 闪光灯开关 开->关->自动** @param mCamera*/private void turnLight(Camera mCamera) {if (mCamera == null || mCamera.getParameters() == null|| mCamera.getParameters().getSupportedFlashModes() == null) {return;}Camera.Parameters parameters = mCamera.getParameters();String flashMode = mCamera.getParameters().getFlashMode();List<String> supportedModes = mCamera.getParameters().getSupportedFlashModes();if (Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)&& supportedModes.contains(Camera.Parameters.FLASH_MODE_ON)) {// 关闭状态parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);mCamera.setParameters(parameters);flashBtn.setImageResource(R.drawable.camera_flash_on);} else if (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {// 开启状态if (supportedModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);flashBtn.setImageResource(R.drawable.camera_flash_auto);mCamera.setParameters(parameters);} else if (supportedModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);flashBtn.setImageResource(R.drawable.camera_flash_off);mCamera.setParameters(parameters);}} else if (Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)&& supportedModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);mCamera.setParameters(parameters);flashBtn.setImageResource(R.drawable.camera_flash_off);}}// 切换前后置摄像头private void switchCamera() {mCurrentCameraId = (mCurrentCameraId + 1) % Camera.getNumberOfCameras();if (camera != null) {camera.stopPreview();preview.setCamera(null);camera.setPreviewCallback(null);camera.release();camera = null;}try {camera = Camera.open(mCurrentCameraId);camera.setPreviewDisplay(mSurfaceView.getHolder());preview.setCamera(camera);camera.startPreview();} catch (Exception e) {Toast.makeText(mContext, "未发现相机", Toast.LENGTH_LONG).show();}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {setResult(0);finish();return true;}return super.onKeyDown(keyCode, event);}@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == PROCESS) {if (resultCode == RESULT_OK) {Intent intent = new Intent();if (data != null) {intent.putExtra(CAMERA_RETURN_PATH,data.getStringExtra(CAMERA_PATH_VALUE2));}setResult(RESULT_OK, intent);finish();} else {if (data != null) {File dir = new File(data.getStringExtra(CAMERA_PATH_VALUE2));if (dir != null) {dir.delete();}}}}}
}

总结

1、网上有些示例代码,担心相机初始化及开启时间较长,将初始化及启动工作单独放在子线程中,偶尔出现黑屏的情况,但也不是经常出现。

导致原因:由于单独开辟了线程去初始化启动相机,导致相机的初始化和开启工作已完成,而找不到画布控件。若出现此情况,可调试或者将线程睡眠500毫秒。

2、按下home键后,再次进入时,为毛黑屏了,如何破?

导致原因:在onCreate中find了SurfaceView,按下Home后程序再次进入时,找不到预览的画布了,可将find的工作放入onResume中,再就是别忘了在onPause中做如下操作:

@Overrideprotected void onPause() {if (camera != null) {camera.stopPreview();preview.setCamera(null);camera.release();camera = null;preview.setNull();}super.onPause();}

本项目源码(Eclipse版):http://download.csdn.net/download/gao_chun/9084853

注:测试机-------> 小米2A、红米、华为P8、华为荣耀3C,魅蓝note2

附:有些小伙伴经常问手机Gif动画如何制作的,在此也分享下:

动画制作小软件GifMaker:http://download.csdn.net/detail/gao_chun/9077023

【转载注明gao_chun的Blog:http://blog.csdn.net/gao_chun/article/details/48246871】

Android相机开发实战相关推荐

  1. camera (19)---Android 相机开发的基本流程

    [Android 相机]Android 相机开发的基本流程 https://blog.csdn.net/bluewindtalker/article/details/54563910 相机开发现在有2 ...

  2. Android NFC开发实战详解

    Android NFC开发实战详解 Android开发实战详解NFC国内第一本AndroidNFC开发书籍带你开启AndroidNFC开发的神秘之旅大综合案例帮助读者快速进入实战角色:WiFi快速连接 ...

  3. 《Android Studio开发实战 从零基础到App上线(第2版)》出版后记

    2015年11月23日,鄙人在csdn发表了第一篇技术文章,掐指一算距今已有三年.遥想当年开写博客,只是为了总结经验同时分享出来,后来机缘巧合受到出版社编辑邀请并出了书,完全是无心插柳的结果.当初写作 ...

  4. 《Android Studio开发实战 从零基础到App上线》源码运行问题解答

    本书提供了所有章节的完整源码下载,自上市以来陆续收到读者的意见反馈,现将与源码有关的问题汇总归类如下,方便更多的朋友解决源码运行过程中发现的问题: 一.打开本书源码时,出现"Plugin w ...

  5. android 相机编程,Android相机开发系列

    Android Camera Develop Series 简介 Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做图 ...

  6. 《Android Studio开发实战》学习(五) - 截图

    <Android Studio开发实战>学习(五) - 截图 背景 页面布局 布局文件的编写 代码文件的编写 ImageView控件截图的原理 运行结果 背景 在这里继续学习Android ...

  7. 《Android Studio开发实战》学习(二)- 聊天室

    <Android Studio开发实战>学习(二)- 聊天室 背景 聊天室布局文件的编写 聊天室代码文件的编写 运行结果 背景 在前一篇文章 1中实现了使用Android Studio开发 ...

  8. Android相机开发和遇到的坑

    转载请标明出处:http://blog.csdn.net/xx326664162/article/details/53350551 文章出自:薛瑄的博客 在Android相机开发实际开发过程中遇到了不 ...

  9. 《Android Studio开发实战 从零基础到App上线》第一版的资源下载和内容勘误

    资源下载 下面是<Android Studio开发实战 从零基础到App上线>(第一版)一书用到的工具和代码资源: 1.本书使用的Android Studio版本为2.2.3,因为Andr ...

  10. Android项目开发实战常用知识点

    Android项目开发实战常用知识点 一:启动页延时两秒再跳转到主界面: //执行类 private class SlpashRunnable implements Runnable {@Overri ...

最新文章

  1. python网络编程--socket简单实现
  2. android中SELINUX规则分析和语法简介
  3. I'm genius,用游戏柄控制鼠标
  4. 脚本升级openssh
  5. 推荐一款基于bootstrap的漂亮的前端模板——inspinia_admin
  6. awg线规,直径,面积,电流对照
  7. 代码 点胶gcode_Gcode代码解释
  8. 自组织神经网络算法-SOM
  9. Freeswitch和微信小程序对接
  10. kali 2020.3中安装pyrit无法定位软件包
  11. leetcode和牛客网刷题
  12. Unity-ShaderGraph学习笔记第一步: 如何打开ShaderGraph 制作全息效果Shader
  13. linux查询网卡是百兆还是千兆,查看网卡是百兆还是千兆
  14. 042-16 Backup and Recovery备份与恢复(前3种备份方式)
  15. 16、持续集成流水线实践:流水线上的AI单元测试(MAVEN)
  16. 企业上云进程加速,云计算或将迎来发展黄金期
  17. 用c语言写一段英文对话,三年级英语对话
  18. Mono入门教程(一)--------------C++中整合Mono
  19. IbBBX24–IbTOE3–IbPRX17模块通过清除甘薯中的活性氧来增强甘薯对非生物胁迫耐受性
  20. 3D游戏引擎系统源码C++本科毕业设计,C++ 3D引擎源码,渲染系统使用的OpenGL 及 OpenGL ES

热门文章

  1. Maven的核心概念(五分钟快速掌握)
  2. HTTP缓存机制在iOS中的应用和体现
  3. lock concurrence
  4. Codeforces Round #558 Div.2 - C2 - Power Transmission (Hard Edition)
  5. Oracle 11gR2 RAC的两个bug
  6. 【转】flash air中读取本地文件的三种方法
  7. Matlab中struct的用法
  8. onCreateView中加载大位图
  9. Ubuntu 12.04 LTS 键盘快捷键(转)
  10. matlab 中.*和* 区别