老规矩,先上一下项目地址:GitHub:https://github.com/xiangzhihong/CameraDemo
方式:
- 调用Camera API 自定义相机
- 调用系统相机
由于需求不同,所以选择的方案固然也不同,至于第二种调用系统相机,这里就不过多讲解了,使用Intent对象设置一个Action动作即可,跳转时使用startActivityForResult,然后在onActivityResult处理相关数据便可,关键代码:
- intent.setAction("android.media.action.STILL_IMAGE_CAMERA");
至于使用,较常见的一般是应用中用户上传头像的时候调用,然后返回处理图像数据。
而第一种自定义相机的方式使用也十分普遍,但是要做好这个模块,相对来说还是有一定难度的,之前分享过一个Github上的开源相机的项目,项目由美国的一个团队开发,集 拍照、摄影、各种特效动画 等功能与一身,本人之前研究了下,发现功能比较全面也很强大,抠出来单独拍照那一个模块,我滴妈呀,真TM费劲!相机不管是预览还是拍摄图像都还是很清晰的,自己当时也写了一个,比较操蛋,只能怪自己对这一块的优化了解浅显吧!特别是预览的时候,聚焦完成后,焦点周边会出现很多白色的噪点,密密麻麻,特别严重,头疼的很。不过也总算解决了,灰常感谢USA的那个什么什么团队的开源相机程序。经过自己改造后的预览效果图:
下面看下这个项目的效果图,我也把地址甩底下,大伙感兴趣的自行Clone研究(或者闲的蛋疼也可以抽时间剥离开每一个模块学习,作为日后的知识储备),里面也用到了这个Android中读取图片EXIF元数据之metadata-extractor的使用。
相机开发简介
下面说说在Android中调用Camera来定义相机的最基本步骤:
- 打开相机 —— 调用Camera的open()方法。
- 获取拍照参数 —— 调用Camera的getParameters()方法,返回Camera.Parameters对象。
- 拍照参数设置 —— 调用Camera.Parameters对象。
- 拍照参数控制 —— 调用Camera的setParameters(),并将Camera.Parameters对象作为参数传入。注:Android2.3.3之后不用设置。
- 预览取景 —— 调用Camera的startPreview()方法,在之前注意调用Camera的setPreviewDisplay(SurfaceHolder holder)设置使用哪个SurfaceView来显示取得的图片。
- 拍照 —— 调用Camera的takePicture()
- 停止预览 —— 调用Camera的stopPreview()方法
- 资源释放 —— 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类,其中封装了我们需要的大部分功能,下面做个简单介绍:
- setPictureFormat() 方法用于设置相机照片的格式,其参数是一个字符型参数,位于PixelFormat类中,如:PixelFormat.JPEG。
- setSceneMode() 方法用于设置相机场景类型,其参是是一个字符型参数,位于Parameters类中,以SCENE_MODE_开头。
- setZoom() 方法用于设置相机焦距,其参数是一个整型的参数,该参数的范围是0到Camera.getParameters().getMaxZoom()。
- setPictureSize() 方法用于设置相机照片的大小,参数为整型。
- setWhiteBalance() 方法用于设置相机照片白平衡,其参数是一个字符型,位于Parameters类中,以WHITE_BALANCE开头。
- setJpegQuality() 方法用于设置相机照片的质量,其参数是一个整型参数,取值范围为1到100。
- setFlashMode() 方法用于设置闪光灯的类型,其参数是一个字符型参数,位于Parameters类中,以FLASH_MODE_开头。
- 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" >
- <!-- 预览画布 -->
- <SurfaceView
- android:id="@+id/surfaceView"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <!-- 闪光灯、前置摄像头、后置摄像头、聚焦 -->
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <org.gaochun.camera.CameraGrid
- android:id="@+id/camera_grid"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentTop="true" />
- <View
- android:id="@+id/focus_index"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:background="@drawable/camera_focus"
- android:visibility="invisible" />
- <ImageView
- android: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" />
- <ImageView
- android: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" />
- <!-- 底部按钮 -->
- <RelativeLayout
- android:layout_width="fill_parent"
- android:layout_height="70dp"
- android:layout_alignParentBottom="true"
- android:background="#a0000000"
- android:padding="5dp" >
- <Button
- android: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" />
- <ImageView
- android: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" />
- <Button
- android: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 mode
- params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
- // set Camera parameters
- mCamera.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-facing
- result = (info.orientation - degrees + 360) % 360;
- }
- camera.setDisplayOrientation(result);
- }
- @Override
- protected 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);
- // }
- }
- @Override
- protected 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>() {
- @Override
- public 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>() {
- @Override
- public 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 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;
- }
- public void reAutoFocus() {
- if (isSupportAutoFocus) {
- mCamera.autoFocus(new Camera.AutoFocusCallback() {
- @Override
- public 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() {
- @Override
- public void run() {
- try {
- sleep(100);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (mCamera == null) {
- return;
- }
- mCamera.autoFocus(new Camera.AutoFocusCallback() {
- @Override
- public 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;
- }
- @Override
- public 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;
- @Override
- public 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) {
- }
- }
- }
- @Override
- protected 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();
- }
- }
- }
- @Override
- protected 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();
- }
- };
- @Override
- public 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() {
- @Override
- public void run() {
- focusIndex.setVisibility(View.INVISIBLE);
- }
- }, 800);
- return false;
- }
- @Override
- public 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~11
- sb.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;
- }
- @Override
- protected String doInBackground(Void... params) {
- // Write to SD Card
- String path = "";
- try {
- showProgressDialog("处理中");
- path = saveToSDCard(data);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- }
- return path;
- }
- @Override
- protected 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() {
- @Override
- public void run() {
- if (mAlertDialog != null && mAlertDialog.isShowing()
- && !CameraActivity.this.isFinishing()) {
- mAlertDialog.dismiss();
- mAlertDialog = null;
- }
- }
- });
- }
- private void showProgressDialog(final String msg) {
- this.runOnUiThread(new Runnable() {
- @Override
- public 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();
- }
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
- setResult(0);
- finish();
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
- @Override
- public 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中做如下操作:
- @Override
- protected 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
注:这代码有些问题,我会在后面贴上最新的优化代码,欢迎继续支持