我想使用自定义相机拍摄比预览尺寸更小的照片,我听说保持纵横比非常重要。我已经使用了PreviewFrameLayout
in AOSP ICS https://android.googlesource.com/platform/packages/apps/Camera/+/ics-factoryrom-2-release/src/com/android/camera/PreviewFrameLayout.java我发现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>
EDIT:
纵向:
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);
}
}
EDIT:
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.
添加了一个问题来解决这个问题使用 FrameLayout 时相机预览会拉伸 https://stackoverflow.com/questions/21255172/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 的横向拍照并预览......
我发现,尽管使用了ExifInterface
and Matrix
修复此轮换代码ImagePreviewActivity
Exif方向始终为0,我上次修复了这个问题(没有使用PreviewFrameLayoutParameters.setRotation
设置方向时的代码...但它似乎在这里不起作用