Android Camera 预览拉伸问题

Posted 胡刚2021

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Camera 预览拉伸问题相关的知识,希望对你有一定的参考价值。

预览会出现拉伸的问题代码:
预览拉伸Demo


预览画面拉伸问题改正的 gitee 代码在文章最后给出
预览拉伸的原因:
每颗Camera都支持一些分辨率,Camera的这些图像如果和预览画面的宽高比例不一致就会发生预览图像拉伸。注意这里是宽高比,只要宽高比一致,即使宽高的大小不一样也不会拉伸。
另外:旋转手机时 Camera 也会旋转,但是预览画面不会旋转,这会导致 Camera 图像原来的宽映射到预览画面的高, Camera 图像原来的高映射到预览画面的宽。由于手机画面的尺寸一般都不是 1 :1 的,所以当手机旋转时,预览画面一定会产生拉伸的效果。

预览拉伸的解决办法:
将预览画面的宽高比设置为和 Camera 输出画面的宽高比一致

步骤
1.列出这颗 Camera 支持的所有 Size,以及手机的 width 和 height
2.遍历选择一个 Size,算出这个 Size 的宽高比 ratio = size.height / size.width(因为Camera Size的宽高是相反的)
3.以手机的宽作为预览控件的宽,如果得出的 ratio 使得控件的 height 超过手机的 height ,那么 遍历下一个Size 重复步骤2,直到选出一个合适的 Size。如果所有的 Size 的高都比手机的高都大,那么就需要使用 Matrix 手动将 Camera 输出的画面缩放到一个合适的大小,最后这种情况先不实现。
两点注意:
1.Camera 支持的所有 Size ,它的 size.width 和 size.height 是反着的,我们要把 size.width 当成高 ,size.height 当成宽。
2.预览控件在设置宽高比的时候,要保证预览画面的宽和手机的宽是一样的,这样看起来才不别扭。

预览控件的宽应该设置为 match_parent,高应该设置为 wrap_content
假设要设置的宽高比 ratio = cameraWidth / cameraHeight,
预览控件在设置宽高比的时候只改变高度即可: height = width / ratio

也许你会发现,这里我们并没有针对横屏的预览显示做处理,实际测试中发现,我们设置完宽高比,在旋转到横屏的时候也不会发生预览拉伸。


关键代码:

private Size getBestSize() 
	CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
	CameraCharacteristics characteristics = null;
	try 
	    characteristics = cameraManager.getCameraCharacteristics("0");
	 catch (CameraAccessException e) 
	    e.printStackTrace();
	
	
	// 获得手机的宽高
	Point point = new Point();
	((Activity) context).getWindowManager().getDefaultDisplay().getRealSize(point);
	int phone_width = point.x;
	int phone_height = point.y;
	Log.e(TAG, "phone_width = "+phone_width+", phone_height = "+phone_height );
	// 获取这颗Camera支持的Size
	StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
	ArrayList<Size> sizeList = new ArrayList<Size>(Arrays.asList(map.getOutputSizes(SurfaceTexture.class)));
	for (Size size : sizeList) 
	    Log.e(TAG, "width = "+size.getWidth() +", height = "+size.getHeight());
	    double ratio = (double) size.getHeight() / (double)size.getWidth();
	    // 以手机的宽作为控件的宽,计算出控件的高
	    int height = (int) (phone_width / ratio);
	    Log.e(TAG, "ratio = " + ratio + ", height = " + height);
	    // 如果计算出的控件的高超过手机的高,就遍历下一个
	    if (height > phone_height) 
	        continue;
	    
	    else 
	        return size;
	    

// 没有合适的宽高比
return null;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.TextureView;

public class AutoFitTextureView extends TextureView 
    private static final String TAG = "hugang";

    private int ratioW = 0;
    private int ratioH = 0;

    public AutoFitTextureView(Context context) 
        super(context);
    

    public AutoFitTextureView(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    /**
     * 设置宽高比
     * @param width
     * @param height
     */
    public void setAspectRation(int width, int height)
        if (width < 0 || height < 0) 
            throw new IllegalArgumentException("width or height can not be negative.");
        
        ratioW = width;
        ratioH = height;
        Log.e(TAG, "setAspectRation: ratioW = "+ratioW+" ratioH = "+ratioH );
        //请求重新布局
        requestLayout();
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        Log.e(TAG, "onMeasure: width = "+width+" height = "+height );
        if (0 == ratioW || 0 == ratioH)
            //未设定宽高比,使用预览窗口默认宽高,当AutoFitTextureView初始化的时候会走一次这个逻辑
            setMeasuredDimension(width, height);
            Log.e(TAG, "onMeasure 000: width = "+width+" height = "+height );
         else 
            //设定宽高比,调整预览窗口大小(调整后窗口大小不超过默认值)
            if (width < height * ratioW / ratioH) 
                Log.e(TAG, "onMeasure 111: width = "+width+" height = "+(width * ratioH / ratioW) );
                setMeasuredDimension(width, width * ratioH / ratioW);
             else 
                Log.e(TAG, "onMeasure 222: width = "+(height * ratioW / ratioH)+" height = "+height );
                setMeasuredDimension(height * ratioW / ratioH, height);
            
        
    

解决预览画面拉伸的Demo
预览画面拉伸问题改正Demo

以上是关于Android Camera 预览拉伸问题的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin android Camera2 - 在 18:9 纵横比设备上拉伸图像预览

在android中将相机预览拉伸到全屏

Android Camera2预览偶尔会旋转90度

Android:更改屏幕上的相机预览大小

Camera2 API预览方面已损坏

完美解决Android使用Zxing扫描二维码改成竖屏后,后面的预览画面出现了拉伸,扭曲的情况