Android相机开发实战

开源分享二(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" >

    <!-- 预览画布 -->
    <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



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



  • 28
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
为了进行Android Studio开发实战攻防,您需要掌握以下几个方面: 1. 反编译APK文件:您可以使用工具如apktool来反编译APK文件,以便查看和修改应用程序的代码和资源文件。 2. 修改应用程序代码:您可以使用反编译后的代码来修改应用程序的行为,例如更改应用程序的逻辑或添加新功能。 3. 调试应用程序:您可以使用Android Studio的调试器来调试应用程序,以便查找和修复应用程序中的错误。 4. 保护应用程序:您可以使用加密和混淆技术来保护应用程序的代码和资源文件,以防止应用程序被反编译和修改。 下面是一个例子,展示如何使用apktool反编译和重新编译APK文件: 1. 反编译APK文件: ```shell java -jar apktool_2.6.1.jar d hello_world.apk ``` 这将在当前目录下创建一个名为hello_world的文件夹,其中包含反编译后的代码和资源文件。 2. 修改应用程序代码: 您可以使用任何文本编辑器来修改反编译后的代码。例如,您可以打MainActivity.java文件并更改onCreate方法中的代码,如下所示: ```java protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.textView); textView.setText("Hello World!"); } ``` 您可以将“Hello World!”更改为任何您想要的文本。 3. 重新编译APK文件: ```shell java -jar apktool_2.6.1.jar b hello_world -o hello_world_modified.apk ``` 这将在当前目录下创建一个名为hello_world_modified.apk的文件,其中包含您修改后的应用程序代码和资源文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值