【发布时间】:2014-01-20 05:12:33
【问题描述】:
我想使用自定义相机拍摄小于预览尺寸的照片,我听说保持纵横比非常重要。我在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添加了一个问题来解决这个问题
编辑2:
Camera PreviewFrameLayout 工作得几乎完美(几乎是全屏)...当我将此代码添加到构造函数时,就在 ShutterButton 上方:
public PreviewFrameLayout(Context c,AttributeSet attrs)
{
super(c,attrs);
setAspectRatio(4.0/3.0);
}
仍然存在屏幕显示方向为90的横向拍照并预览等问题......
我发现尽管使用ExifInterface 和Matrix 在ImagePreviewActivity 中修复此旋转代码,但在横向和纵向模式下,获得的图像都顺时针旋转了 90 度
Exif 方向始终为 0,我上次修复了这个问题(在设置方向时没有使用 Parameters.setRotation 代码的 PreviewFrameLayout...但它似乎在这里不起作用
【问题讨论】:
-
你的屏幕的自然分辨率是多少,你的代码写了什么来记录
PreviewFrameLayoutwidth和height? -
宽度:720 高度:540。我的 Galaxy Nexus 屏幕的自然分辨率是 WXGA (1280 x 800)。虽然
getSupportedPreviewSize()似乎也支持更大的分辨率 -
您能查看
getPaddingLeft()及其兄弟姐妹报告的内容吗? -
@AlexCohn PreviewFrameLayout:左:0 右:0 上:0 下:0
-
@AlexCohn 我也尝试过使用最大的图片尺寸like this。预览仍然出现在一个框中
标签: java android android-camera surfaceview aspect-ratio