相机预览未全屏显示(显示在框中)

时间:2014-01-20 05:12:33

标签: java android android-camera surfaceview aspect-ratio

我想使用自定义相机拍摄尺寸小于预览的照片,我听说保持纵横比非常重要。我使用了AOSP ICS中的PreviewFrameLayout。我发现SurfaceView只显示在一个框中活动的中心,并没有填满整个屏幕。

public class PreviewFrameLayout extends RelativeLayout{

static final String TAG="PreviewFrameLayout";
public interface OnSizeChangedListener
{
    public void onSizeChanged(int w,int h);
}

OnSizeChangedListener mListener;

public void setOnSizeChangedListener(OnSizeChangedListener listener)
{
    mListener=listener;
}

private double mAspectRatio=4.0/3.0;

public PreviewFrameLayout(Context c,AttributeSet attrs)
{
    super(c,attrs);
}

public void setAspectRatio(double ratio)
{
    if(ratio<=0.0)
        throw new IllegalArgumentException();
    if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
        ratio=1/ratio;
    if(mAspectRatio!=ratio)
    {
        mAspectRatio=ratio;
        requestLayout();
    }
}

@Override
protected void onMeasure(int widthSpec,int heightSpec)
{
    int previewWidth=MeasureSpec.getSize(widthSpec);
    int previewHeight=MeasureSpec.getSize(heightSpec);

    int hPadding=getPaddingLeft()+getPaddingRight();
    int vPadding=getPaddingTop()+getPaddingBottom();

    previewWidth-=hPadding;
    previewHeight-=vPadding;

    if(previewWidth>previewHeight*mAspectRatio)
        previewWidth=(int) (previewHeight*mAspectRatio+.5);
    else
        previewHeight=(int)(previewWidth/mAspectRatio+.5);
    previewWidth+=hPadding;
    previewHeight+=vPadding;
    Log.d(TAG,"Aspect ratio "+mAspectRatio);
    Log.d(TAG, "Width: "+previewWidth+" Height: "+previewHeight);
    super.onMeasure(MeasureSpec.makeMeasureSpec(previewWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(previewHeight, MeasureSpec.EXACTLY));
}

protected void onSizeChanged(int w,int h,int oldw,int oldh)
{
    if(mListener!=null)
        mListener.onSizeChanged(w, h);
}
}

并设置了预览和图片大小:

  private Size getOptimalPreviewSize(List<Size> supportedSizeList,double targetRatio)
{
    final double ASPECT_TOLERANCE=0.05;
    double minDiff=Double.MAX_VALUE;
    Size optimalSize=null;
    Display display=getWindowManager().getDefaultDisplay();
    @SuppressWarnings("deprecation")
    int targetHeight=Math.min(display.getWidth(), display.getHeight());
    if(targetHeight<=0)
    {   
        WindowManager windowManager=(WindowManager)getSystemService(Context.WINDOW_SERVICE);
        targetHeight=windowManager.getDefaultDisplay().getHeight();
    }
    for(Size size:supportedSizeList)
    {
        double ratio=(double)size.width/size.height;
        if(Math.abs(targetRatio-ratio)>ASPECT_TOLERANCE)
            continue;
        if(Math.abs(size.height-targetHeight)<minDiff)
        {
            optimalSize=size;
            minDiff=Math.abs(size.height-targetHeight);
        }
    }

    for(Size size:supportedSizeList)
    {
        if((Math.abs(size.height-targetHeight))<minDiff)
        {   
            optimalSize=size;
            minDiff=Math.abs(size.height-targetHeight);
        }
    }
    return optimalSize;
}

private Size getDesiredPictureSize(List<Size> supportedSizeList)
{
    //Resolution is widthxheight

    Size result=null;
    final int minArea=500*500;  
    final int maxArea=1000*1000;
    for(Size size:supportedSizeList)
    {
        if(size.width*size.height>minArea && size.width*size.height<maxArea)
        {
            if(result==null)
                result=size;
            else
            {
                int resultArea=result.width*result.height;
                int sizeArea=size.width*size.height;
                if(resultArea<sizeArea)
                {   
                    result=size;
                }   
            }
        }
    }
    return result;
 }

我使用此处的代码将宽高比设置为图片大小:

    mParameters=mCamera.getParameters();
    List<Size> supportedPictureSizes=mParameters.getSupportedPictureSizes();
    List<Size> supportedPreviewSizes=mParameters.getSupportedPreviewSizes();
    mPictureSize=getDesiredPictureSize(supportedPictureSizes);
    double targetRatio=(double)mPictureSize.width/mPictureSize.height;
    mPreviewPanel=findViewById(R.id.frame_layout);
    mPreviewFrameLayout=(PreviewFrameLayout)findViewById(R.id.frame);
    mPreviewFrameLayout.setAspectRatio(targetRatio);

此布局的父级是RelativeLayout

<com.example.newcameraproject.PreviewFrameLayout android:id="@+id/frame"
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <SurfaceView android:id="@+id/camera_preview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
      </com.example.newcameraproject.PreviewFrameLayout>

这包含在主摄像机布局中,如下所示:

  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>

 <include layout="@layout/preview_frame"/>

 </LinearLayout>

编辑:

纵向:

CameraActivity(6502): Resolution Width: 720 Resolution Height: 1184
CameraActivity(6502): The Picture Width: 1152 The Picture Height: 864
CameraActivity(6502): The Preview Width:960 Preview Height: 720
CameraActivity(6502): Picture Ratio: 1.3333333333333333
CameraActivity(6502): Preview Ratio: 1.3333333333333333
PreviewFrameLayout(6502): Left: 0 Right: 0 Top: 0 Bottom: 0
PreviewFrameLayout(6502): Aspect ratio 0.75
PreviewFrameLayout(6502): Width: 720 Height: 960

宽高比被反转,因为它处于纵向方向。

if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
        ratio=1/ratio;

相机显示为比活动小的框。

景观方向:

  CameraActivity(6502): Resolution Width: 1196 Resolution Height: 720
  CameraActivity(6502): The Picture Width: 1152 The Picture Height: 864
  CameraActivity(6502): The Preview Width:960 Preview Height: 720
  CameraActivity(6502): Picture Ratio: 1.3333333333333333
  CameraActivity(6502): Preview Ratio: 1.3333333333333333
  PreviewFrameLayout(6502): Left: 0 Right: 0 Top: 0 Bottom: 0
  PreviewFrameLayout(6502): Aspect ratio 1.3333333333333333
  PreviewFrameLayout(6502): Width: 787 Height: 590

 mPreviewSize=getOptimalPreviewSize(this,supportedPreviewSizes, targetRatio);
    Log.d(TAG, "The Preview Width:"+mPreviewSize.width+" Preview Height: "+mPreviewSize.height);
    double ratio=(double)mPreviewSize.width/mPreviewSize.height;
    Log.d(TAG,"Picture Ratio: "+targetRatio);
    Log.d(TAG, "Preview Ratio: "+ratio);
    int new_width=0, new_height=0;    
 if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_PORTRAIT)
    {
        if((double)previewFrame.getWidth()/previewFrame.getHeight()<ratio)
        {
            new_width=(int)(Math.round(previewFrame.getHeight()*ratio));
            new_height=getWindowManager().getDefaultDisplay().getHeight();
        }
        else
        {
            new_width=getWindowManager().getDefaultDisplay().getHeight();
            new_height=(int)Math.round((double)new_width/ratio);
        }
    }
    if(getResources().getConfiguration().orientation==Configuration.ORIENTATION_LANDSCAPE)
    {
        if((double)previewFrame.getWidth()/previewFrame.getHeight()<ratio)
        {
            new_width=(int)(Math.round(previewFrame.getHeight()*ratio));
            new_height=getWindowManager().getDefaultDisplay().getHeight();
        }
        else
        {
            new_width=getWindowManager().getDefaultDisplay().getWidth();
            new_height=(int)Math.round((double)new_width/ratio);
        }
    }

编辑:

1.Ensured that getOptimalPreviewSize and other methods works,changed code outside of this post to do so.
2.Even though these methods now work,the preview was not filling the screen,so changed to FrameLayout.The final version(see above) now works for both landscape and portrait.
3.I have noticed that the image in the preview looks stretched when the Camera Activity is rotated to landscape.

Camera Preview stretched when using FrameLayout

添加了一个问题以解决此问题

EDIT2:

Camera PreviewFrameLayout几乎完美地工作(它几乎全屏)...当我将此代码添加到构造函数时,就在ShutterButton上方:

 public PreviewFrameLayout(Context c,AttributeSet attrs)
{
    super(c,attrs);
    setAspectRatio(4.0/3.0);
}

仍有一些问题,比如在屏幕显示方向为90的横向拍照并预览...

尽管使用ExifInterfaceMatrix来修复ImagePreviewActivity

中的旋转代码,但我发现所获得的图像在横向和纵向模式下都顺时针旋转90度

Exif方向始终为0,我最后一次修复了这个问题(设置方向时没有使用Parameters.setRotation代码的PreviewFrameLayout ...但它似乎没有在这里工作

1 个答案:

答案 0 :(得分:1)

我看不到调用getOptimalPreviewSize()的位置;另外,没有真正的理由设置预览尺寸以匹配屏幕;通常,制造商会选择默认的相机预览,使其在屏幕上看起来很好(包括缩放)。

尽管如此,您发布的代码可以大大简化。如果我理解正确,你在活动中修改了screenOrientation,并希望用相机预览填充所有屏幕,保留纵横比以匹配所需的图片大小。

This simple example,显示如何设置预览曲面视图的宽度和高度。如果这还不够,请随时询问更多细节。