Android用摄像头的那点破事

By | 2011/07/29

摄像头这两天玩Android玩的废寝忘食,Blog都好几天没加东西了,惭愧!记录一下这两天最崩溃的一个问题。

好早就装了开发环境,真正着手还是这两天,非常的生疏,虽然有SDK文档,那么多蚊子一般的字,实在没心思慢慢研究。这不想调用摄像头,原以为很容易就能搞定的,累计花了大概有一天的时间才只能保证不出错……至于效果嘛,难说啊!

先看API-examples里有调用 摄像头的例子,在模拟器上虽然看不出什么效果,毕竟还是能执行的,就是一个方块在黑白相间的背景上移动呗。

就这么一个Google提供的范例,传到我的HTC G2上也能一执行就报错,我对Google的尊敬之情顿时减少了0.0001%啊……(当然有可能是G2不够标准,但毕竟其他的软件都是能用的,看来是有不少健壮代码了啊)。联机调试看了一下,出错的这一行(android-7里的):

parameters.setPreviewSize(w, h);

查一下,摄像头不是所有随便的(w, h)都能够认识的,所以呢,我们有了下面这样的增强版:

List<Size> mSupportedPreviewSizes;
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);

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;
    }

后来的Sample里有了这段代码,看起来强大了不少。然而非常不幸的,首先getSupportedPreviewSizes()这个函数在2.1之后才有,我一开始是打算用1.6开发的……好吧我改,这个先不说,自己的手机已经刷到2.1了,这个函数的返回值居然是null?!如果确实想老版本上也用的话,怎么办??

有鉴于有软件可以达成,所以肯定是有方法的!得这么写:

public class SupportedSizesReflect {
	private static Method Parameters_getSupportedPreviewSizes = null;	
	private static Method Parameters_getSupportedPictureSizes = null;

	static {
		initCompatibility();
	};

	private static void initCompatibility() {
		try {
			Parameters_getSupportedPreviewSizes = Camera.Parameters.class.getMethod(
					"getSupportedPreviewSizes", new Class[] {});

			Parameters_getSupportedPictureSizes = Camera.Parameters.class.getMethod(
					"getSupportedPictureSizes", new Class[] {});

		} catch (NoSuchMethodException nsme) {
			nsme.printStackTrace();
			Parameters_getSupportedPreviewSizes = Parameters_getSupportedPictureSizes = null;			
		}		
	}

	/**
	 * Android 2.1之后有效
	 * @param p 
	 * @return Android1.x返回null
	 */
	public static List<Size> getSupportedPreviewSizes(Camera.Parameters p) {
		return getSupportedSizes(p, Parameters_getSupportedPreviewSizes);
	}

	public static List<Size> getSupportedPictureSizes(Camera.Parameters p){
		return getSupportedSizes(p, Parameters_getSupportedPictureSizes);
	}	

	@SuppressWarnings("unchecked")
	private static List<Size> getSupportedSizes(Camera.Parameters p, Method method){
		try {
			if (method != null) {
				return (List<Size>) method.invoke(p);
			} else {
				return null;
			}
		} catch (InvocationTargetException ite) {
			Throwable cause = ite.getCause();
			if (cause instanceof RuntimeException) {
				throw (RuntimeException) cause;
			} else if (cause instanceof Error) {
				throw (Error) cause;
			} else {
				throw new RuntimeException(ite);
			}
		} catch (IllegalAccessException ie) {
			return null;
		}
	}	
}

啊啊~,リフレクションなんか、大嫌い……然后还要用类似这样的方法调用~

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {

	Camera.Parameters params = camera.getParameters();

	List<Size> supportedPictureSizes 
				= SupportedSizesReflect.getSupportedPictureSizes(params);
	List<Size> supportedPreviewSizes 
				= SupportedSizesReflect.getSupportedPreviewSizes(params);

	if ( supportedPictureSizes != null &&
		supportedPreviewSizes != null &&
		supportedPictureSizes.size() > 0 &&
		supportedPreviewSizes.size() > 0) {

		//2.x
		pictureSize = supportedPictureSizes.get(0);

		int maxSize = 1280;
		if(maxSize > 0){
			for(Size size : supportedPictureSizes){    			
				if(maxSize >= Math.max(size.width,size.height)){
					pictureSize = size;
					break;
				}						
			}
		}

		WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);		
		Display display = windowManager.getDefaultDisplay();		
		DisplayMetrics displayMetrics = new DisplayMetrics();
		display.getMetrics(displayMetrics);

		previewSize = getOptimalPreviewSize(
							supportedPreviewSizes, 
							display.getWidth(), 
							display.getHeight()); 

		params.setPictureSize(pictureSize.width, pictureSize.height);		
		params.setPreviewSize(previewSize.width, previewSize.height);								

	}
	this.camera.setParameters(params);
	try {
		this.camera.setPreviewDisplay(holder);
	} catch (IOException e) {
		e.printStackTrace();
	}
	this.camera.startPreview();
}

死机无数次之后总结出来的啊,发现程序写的一个不好强制结束了,摄像头都无法再次启用了,kill都不行,只能重新启动手机才好。重启一次还那么慢,谁知道有比较适合G2的row?

哦还有一个,预览画面90°的,2.X后可以用parameters.set(“rotation”, “90”),之前的话得写成parameters.set(“orientation”, “portrait”)。但是据说不是所有的机器都可以的……

3 thoughts on “Android用摄像头的那点破事

  1. Pingback: 给Camera增加一个pictureSize选项 - Android - 开发者第911887个问答

  2. AFAFSAF

    大哥,你想要说什么??????????????????????????????

    Reply

发表评论

您的电子邮箱地址不会被公开。