首页 > 代码库 > Android 自定义照相机拍照截图并仿照nice添加标签

Android 自定义照相机拍照截图并仿照nice添加标签

项目中最近需要用到摄像机拍照,起初的时候肯定怎么省事怎么来,直接就是调用系统的摄像机了,那么问题来了,调用系统摄像机的时候,发现不同的手机拍摄出的照片的旋转角度不一样。举个例子来说,小米手机拍摄出的照片旋转角度为0度(照片是正的),三星手机拍摄出的照片,照片用同样的方法加载出来,而照片旋转了90度,(照片是反的),shit,这是为什么?

刚开始的时候肯定将照片旋转一定的角度来解决,但是发现这样有一点low啊,并且设计上要求在拍照界面中要自己绘制一个框框,用户看到这个框框后拍照,拍照后自动截取图片,并压缩到640*640,shit,这样以来调用系统的照相机肯定就是不行了啊。。。。。

没有办法了,只能到网上去搜索相关的解决方案了,但是搜索了好久基本上就是简单的预览,哎,最后到google官方的例子里面发现了一个比较好的SurfaceView,这个SurfaceView起码根据你的手机计算出了最合适的拍照比例,好吧。这还是不够的,还是太简单了。最后通过多方途径(参考以前项目的实现,问一个大哥),大概明白了摄像机的基本原理。

下面简单的大致描述一下摄像机:

1:我们要想拍照正确,自定义的拍照界面的屏幕现实方式要是横屏拍摄,最好设置为全屏并且没有标题,这样拍摄出的照片才是正确,否则你就哭去吧,这也是血泪摸索出来的,下面简单粘贴一个配置文件:

<activity
                android:name="com.example.AndroidCaptureCropTags.camera.ActivityCapture"
                android:configChanges="keyboardHidden|orientation|screenSize"
                android:screenOrientation="landscape"
                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
                android:windowSoftInputMode="adjustResize|stateHidden">
        </activity>

2:其实我们拍照预览的时候,google把摄像头封装了一系列的API,主要是再预览之前计算最佳的预览比例,防止预览的时候变形。并计算最佳的拍摄出的图片的现实比例,这样拍摄出的图片也不会变形。

3:接下来就是摄像机界面白色框框的绘制,就是自定义了一个View而已。

4:最后拍摄出来照片后根据屏幕中显示的白色的框框来截取图片(这个地方也挺蛋疼的。。。);

pictureCallBack = new PictureCallback() {
			@Override
			public void onPictureTaken(byte[] data, Camera camera) {
				_isCapturing = false;
				Bitmap bitmap = null;
				try {
					BitmapFactory.Options options = new BitmapFactory.Options();
					options.inJustDecodeBounds = true;
					BitmapFactory.decodeByteArray(data, 0, data.length, options);
					//					Debug.debug("width--:" + options.outWidth + "  height--:" + options.outHeight);
					options.inJustDecodeBounds = false;
					options.inPreferredConfig = Bitmap.Config.ARGB_8888;
					//此处就把图片压缩了
					options.inSampleSize = Math.max(options.outWidth
							/ kPhotoMaxSaveSideLen, options.outHeight
							/ kPhotoMaxSaveSideLen);
					bitmap = BitmapUtil.decodeByteArrayUnthrow(data, options);
					if (null == bitmap) {
						options.inSampleSize = Math.max(2, options.inSampleSize * 2);
						bitmap = BitmapUtil.decodeByteArrayUnthrow(data, options);
					}
				} catch (Throwable e) {
				}
				if (null == bitmap) {
					Toast.makeText(ActivityCapture.this, "内存不足,保存照片失败!", Toast.LENGTH_SHORT).show();
					return;
				}
				//long start = System.currentTimeMillis();
				Bitmap addBitmap = BitmapUtil.rotateAndScale(bitmap, _rotation, kPhotoMaxSaveSideLen, true);
				Bitmap finalBitmap = cropPhotoImage(addBitmap);
				File photoFile = PathManager.getCropPhotoPath();
				boolean successful = BitmapUtil.saveBitmap2file(finalBitmap, photoFile, Bitmap.CompressFormat.JPEG, 100);
				while (!successful) {
					successful = BitmapUtil.saveBitmap2file(finalBitmap, photoFile, Bitmap.CompressFormat.JPEG, 100);
				}
				if (finalBitmap != null && !finalBitmap.isRecycled()) {
					addBitmap.recycle();
				}
				Intent intent = new Intent();
				intent.putExtra(kPhotoPath, photoFile.getAbsolutePath());
				ActivityCapture.this.setResult(RESULT_OK, intent);
				ActivityCapture.this.finish();
			}
		};
上面代码的逻辑是:拍摄后系统会将图片的数据以byte[]的形式传递给我们,options.inJustDecodeBounds = true;先用这种形式避免直接将图片加载进内存中,得到了图片的宽度和高度后,然后计算图片的inSampleSize,这里自己写了一个最大的边长1600,通过实验发现定义成这个边长后,拍摄出的照片无论是手机上看还是电脑上看,体验效果都是不错的,这也是通过询问以前的大哥才了解的,(我想了半天也不知道为什么非要定义成1600.。。。)。下面就是将data加载成一个Bitmap,注意此时的Bitmap可能是旋转的,还要调用这个方法Bitmap addBitmap = BitmapUtil.rotateAndScale(bitmap, _rotation, kPhotoMaxSaveSideLen, true);来将图片摆正,其中的_rotation这个角度是我们在拍摄的过程中不断的计算的(这个有些是从网上找的,正在消化中。。。。,自己不是太明白的就不说了,免的说错了误人子弟)。将图片旋转正确后下面就是所见即所得了,截图。。。。。。。

//根据拍照的图片来剪裁
	private Bitmap cropPhotoImage(Bitmap bmp) {
		int dw = bmp.getWidth();
		int dh = bmp.getHeight();
		int height;
		int width;
		if (dh > dw) {//图片竖直方向
			//切图片时按照竖屏来计算
			height = getWindowManager().getDefaultDisplay().getWidth();
			width = getWindowManager().getDefaultDisplay().getHeight();
		} else {//图片是水平方向
			//切图片时按照横屏来计算
			width = getWindowManager().getDefaultDisplay().getWidth();
			height = getWindowManager().getDefaultDisplay().getHeight();
		}
		Rect rect = new Rect();
		int left = (width - cropBorderView.getRect().width()) / 2;
		int top = (height - cropBorderView.getRect().height()) / 2;
		int right = left + cropBorderView.getRect().width();
		int bottom = top + cropBorderView.getRect().height();
		rect.set(left, top, right, bottom);
		float scale = 1.0f;
		// 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
		if (dw > width && dh <= height) {
			scale = width * 1.0f / dw;
		}
		if (dh > height && dw <= width) {
			scale = height * 1.0f / dh;
		}
		// 如果宽和高都大于屏幕,则让其按按比例适应屏幕大小
		if (dw > width && dh > height) {
			scale = Math.max(width * 1.0f / dw, height * 1.0f / dh);
		}
		//如果图片的宽度和高度都小于屏幕的宽度和高度,则放大至屏幕大小
		if (dw < width && dh < height) {
			scale = width * 1.0f / dw;
		}
		Matrix matrix = new Matrix();
		matrix.postScale(scale, scale);
		try {
			Bitmap b2 = Bitmap.createBitmap(bmp, 0, 0, dw, dh, matrix, true);
			if (null != b2 && bmp != b2) {
				bmp.recycle();
				bmp = b2;
			}
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
		}
		try {
			Bitmap b3 = Bitmap.createBitmap(bmp, rect.left, rect.top, rect.width(), rect.height());
			if (null != b3 && bmp != b3) {
				bmp.recycle();
				bmp = b3;
			}
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
		}
		//将图片压缩至640*640
		try {
			Bitmap b4 = Bitmap.createScaledBitmap(bmp, 640, 640, false);
			if (null != b4 && bmp != b4) {
				bmp.recycle();
				bmp = b4;
			}
		} catch (OutOfMemoryError e) {
			e.printStackTrace();
		}
		return bmp;
	}

经过测试发现,拍摄出的照片并不是你在屏幕中看见是竖直方向就是竖直方向,可能拍摄出的照片是竖直方向也可能是水平方向(即照片是竖的还是横的)。这是两种情况要分别来进行处理的。。。下面简单用文字描述一下这两种情况。

1:照片是竖的,而我们的Activity是横屏的。。。所以计算截取框的时候我们要按照屏幕是竖的来计算,才可以确定截取框的正确的位置。

2:照片是横的,我们的Activity也是横屏的。。。所以正常计算就ok。

3:不要想着我们屏幕那么大,拍摄出的照片就是屏幕那么大,而是比屏幕大得多。。。。,这就需要我们缩放到屏幕的大小,这样才可以做到所见即所得嘛,你说是不是?具体的缩放无非就是图片等比例缩放。图片是等比例缩放了,但是可能担心这种情况,要是缩放后比屏幕小一点或者大一点那不就不准确了嘛?这点我刚开始的时候也是担心的,但是我们在代码中计算了拍照的最合适的预览比例,最合适的照片比例,经过很多的手机测试发现都是没有问题的,他们的比例是保持一致的。

4:接下来就是烦人的截图了,最后将图片压缩到640*640,就ok了。

5:明白一点不要重复的造车子,你的心里面就舒服多了。

截图完了就保存到本地的文件里面就ok了。下面就是给图片添加一个Tag什么的,Tag添加点动画,都是一点点调出来的,没什么技术含量,包括正方形的控件的自定义也很简单,直接看看代码就ok了。。。。。

草,我发现我写博客写不了那么的详细。。。shit




Android 自定义照相机拍照截图并仿照nice添加标签