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】



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章