只有经过短暂的延迟相机preVIEW布局是正确的 [英] Camera preview layout is correct only after short delay

查看:96
本文介绍了只有经过短暂的延迟相机preVIEW布局是正确的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用自定义相机preVIEW ViewGroup中实现自定义相机活动。在pre 3.0的Andr​​oid一切工作正常,但是从3.0摄像头preVIEW开始看起来像图片1.如果我添加视频下载或做一些调试和延缓onLayout方法那么一点点的布局被显示为预期(图2)。我有点在这里损失......

I have a custom camera Activity that uses custom CameraPreview ViewGroup implementation. On pre 3.0 android everything works fine, but starting from 3.0 camera preview looks like in Picture 1. If I add Thread.sleep or do some debugging and delay onLayout method for a little bit then layout gets displayed as expected (Picture 2). I'm a bit at a loss here...

相机preVIEW类:

CameraPreview class:

public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback {
    private final String TAG = "CameraPreview";

    private boolean mPreviewRunning = false;

    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private Size mPreviewSize;
    private List<Size> mSupportedPreviewSizes;
    private Camera mCamera;

    public boolean IsPreviewRunning() {
        return mPreviewRunning;
    }

    public CameraPreview(Context context) {
        this(context, null, 0);
    }

    public CameraPreview(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CameraPreview(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {            
            requestLayout();
        }
    }

    public void switchCamera(Camera camera) {
       setCamera(camera);
       try {
           camera.setPreviewDisplay(mHolder);
       } catch (IOException exception) {
           Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
       }
       Camera.Parameters parameters = camera.getParameters();
       parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
       requestLayout();

       camera.setParameters(parameters);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // We purposely disregard child measurements because act as a wrapper to a SurfaceView that 
        // centers the camera preview instead of stretching it.
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes == null && mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        }
        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;
            }
            if (previewWidth == 0) {
                previewWidth = 1;
            }
            if (previewHeight == 0) {
                previewHeight = 1;
            }

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

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, acquire the camera and tell it where to draw.
        try {
            if (mCamera != null) {
                Parameters params = mCamera.getParameters();
                mSupportedPreviewSizes = params.getSupportedPreviewSizes();
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        stop();
    }

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

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if (mCamera != null) {
            // Now that the size is known, set up the camera parameters and begin the preview.
            Camera.Parameters parameters = mCamera.getParameters();
            if (mPreviewSize != null) {
                parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            }
            requestLayout();

            mCamera.setParameters(parameters);
            mCamera.startPreview();
            mPreviewRunning = true;
        }
    }

    public void stop() {
        if (mCamera != null) {
            mCamera.stopPreview();
            mPreviewRunning = false;
            mCamera = null;
        }
    }
}

在哪里这个类使用的布局:

The layout where this class is used:

<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="horizontal"
  >
  <FrameLayout android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:layout_weight="2"
  android:padding="5dip"  
  >
      <com.commonlib.controls.CameraPreview
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:layout_weight="1"
      android:layout_gravity="center"
      android:id="@+id/surface_camera"
      />
      <ImageView android:id="@+id/target"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/target"
        >
        </ImageView>
  </FrameLayout>
  <LinearLayout android:layout_width="wrap_content"
  android:layout_height="fill_parent"
  android:paddingLeft="7dip"
  android:orientation="vertical">
    <ImageButton
      android:layout_width="50dip"
      android:layout_height="0dip"
      android:layout_weight="1"
      android:id="@+id/cancel"
      android:src="@android:drawable/ic_menu_revert">
      </ImageButton>
    <ImageButton
      android:layout_width="50dip"
      android:layout_height="0dip"
      android:layout_weight="1"
      android:id="@+id/ok"
      android:src="@android:drawable/ic_menu_camera">
      </ImageButton>      
  </LinearLayout>
</LinearLayout>

更新 我发现,这只是如果调用活动是在纵向模式下。

UPDATE I found out that it only happens if calling Activity was in portrait mode.

更新2 它看起来是这样的行为是由屏幕旋转动画。摄像头活动使用风景模式。如果调用活动是在肖像模式 - 它会导致屏幕旋转动画,这在某种程度上打乱相机preVIEW控制布局。我期待到如何禁用屏幕旋转动画,这种情况或如何重新布局摄像头的活动。

UPDATE 2 It looks like this behavior is caused by screen rotation animation. Camera activity uses landscape mode. If calling activity is in a portrait mode - it causes screen rotation animation and this somehow messes camera preview control layout. I'm looking into how to disable screen rotation animation for this case or how to re-layout camera activity.

推荐答案

那么作为怀疑,这一切是由旋转动画所致。在旋转过程中onMeasure宽度和高度测量的交换,从而最小边距总是考虑(因为从纵向要去景观)的地方。我解决了它是这样的:

So as suspected, all this was caused by rotation animation. During the rotation onMeasure width and height measurements exchanged their places (because of going from portrait to landscape) and thus the smallest margin was always taken into account. I solved it like this:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // We purposely disregard child measurements because act as a wrapper to a SurfaceView that 
    // centers the camera preview instead of stretching it.
    int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    // we only allow landscape, if height > width - it means that we are in rotation animation. Swap width with height
    if (height > width) {
        width += height;
        height = width - height;
        width = width - height;         
    }

    setMeasuredDimension(width, height);
    Log.v("Camera", width + "x" + height);

    if (mSupportedPreviewSizes == null && mCamera != null) {
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
    }
    if (mSupportedPreviewSizes != null) {
        mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);            
    }
}

这篇关于只有经过短暂的延迟相机preVIEW布局是正确的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆