我从AOSP借用了PreviewFrameLayout以保持相机的纵横比,它几乎可以工作(它几乎是全屏):
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
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;
public PreviewFrameLayout(Context c,AttributeSet attrs)
{
super(c,attrs);
setAspectRatio(4.0/3.0);
}
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);
}
}
将此项添加到CameraActivity
而非简单FrameLayout
会导致相机在landscape
模式下拍摄的图像在90 degrees clockwise
上旋转Galaxy Nexus
{1}}。为什么会发生这种情况?如何修改PreviewFrameLayout
以使用它?
#INFO
To fix the orientation I use the code:
private void setDisplayOrientation()
{
Log.d(TAG, "Orientation changed fired");
mDisplayRotation=getDisplayRotation(this);
mDisplayOrientation=getDisplayOrientation(mDisplayRotation, CameraInfo.CAMERA_FACING_BACK);
if(mCamera!=null)
{
Log.d(TAG,"Setting camera and parameter orientation");
mCamera.setDisplayOrientation(mDisplayOrientation);
if(mParameters!=null)
{
mParameters.setRotation(mDisplayOrientation);
}
else
{
Camera.Parameters params=mCamera.getParameters();
params.setRotation(mDisplayOrientation);
mParameters=params;
mCamera.setParameters(params);
}
}
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int getDisplayOrientation(int degrees, int cameraId) {
// See android.hardware.Camera.setDisplayOrientation for
// documentation.
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
//mCamera.setDisplayOrientation(result);
return result;
}
private int getDisplayRotation(Activity activity) {
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
switch (rotation) {
case Surface.ROTATION_0: return 0;
case Surface.ROTATION_90: return 90;
case Surface.ROTATION_180: return 180;
case Surface.ROTATION_270: return 270;
}
return 0;
}
class MyOrientationEventListener extends OrientationEventListener
{
@Override
public void onOrientationChanged(int orientation)
{
if(orientation==ORIENTATION_UNKNOWN)
return;
mOrientation=roundOrientation(orientation,mOrientation);
int orientationCompensation=getDisplayRotation(CameraActivity.this);
if(mOrientationCompensation!=orientationCompensation)
mOrientationCompensation=orientationCompensation;
setOrientationIndicator(mOrientationCompensation);
}
public MyOrientationEventListener(Context context) {
super(context);
}
public MyOrientationEventListener(Context context,int rate)
{
super(context,rate);
}
private int roundOrientation(int orientation, int orientationHistory) {
boolean changeOrientation = false;
final int ORIENTATION_HYSTERESIS=5;//a tolerance value above the limit
if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
changeOrientation = true;
} else {
int dist = Math.abs(orientation - orientationHistory);
dist = Math.min(dist, 360 - dist);
changeOrientation = (dist >= 45 +ORIENTATION_HYSTERESIS );
}
if (changeOrientation) {
return ((orientation + 45) / 90 * 90) % 360;
}
return orientationHistory;
}
}
Among other places,the `setDisplayOrientation` is called inside the `capture` method before setting the parameters and taking a picture.