开源分享二(Android相机开发实战)
开源分享 一(StickerCamera + 仿微信多图选择)
上篇博文给大家分享了两个非常实用的项目功能模块,不知道大伙感觉如何?有木有一种臭袜子味扑鼻,酸爽的赶脚!!!贱笑贱笑了~ ~
OK!不扯淡了,言归正传。本文将主要为大家介绍Android中自定义相机的开发,做Android应用的童鞋应该都知道,在应用中使用相机功能有两种方式:
由于需求不同,所以选择的方案固然也不同,至于第二种调用系统相机,这里就不过多讲解了,使用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来定义相机的最基本步骤:
开启和关闭预览的联系如下: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类,其中封装了我们需要的大部分功能,下面做个简单介绍:
下面分享本篇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(); }
本项目源码:http://download.csdn.net/download/gao_chun/9084853
附:有些小伙伴经常问手机Gif动画如何制作的,在此也分享下:
动画制作小软件GifMaker:http://download.csdn.net/detail/gao_chun/9077023
【转载注明gao_chun的Blog:http://blog.csdn.net/gao_chun/article/details/48246871】
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/gao_chun/article/details/48246871