Using getOptimalPreviewSize()
is important, but it does not resolve all stretching on all devices in all layouts. You must be prepared to crop the preview a little bit so that the preview fills the screen without distortion.
There are different techniques to force the size of the surface different from the actual screen size, but this one I find the easiest:
I add the CameraView
to my layout:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
public CameraView(Context context, AttributeSet attr) {
super(context, attr);
// install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
getHolder().addCallback(this);
}
@Override public void surfaceCreated(SurfaceHolder holder) {
openCamera();
bCameraInitialized = false;
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {
camera.release();
}
@Override public void surfaceChanged(SurfaceHolder holder,
int format, int w, int h) {
if (bCameraInitialized) {
// we will get here after we have resized the surface, see below
return;
}
cameraSetup(w, h);
bCameraInitialized = true;
}
private void cameraSetup(int w, int h) {
// set the camera parameters, including the preview size
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
double cameraAspectRatio = ((double)optimalSize.width)/optimalSize.height;
if (((double)h)/w > cameraAspectRatio) {
lp.width = (int)(h/cameraAspectRatio+0.5);
lp.height = h;
}
else {
lp.height = (int)(w*cameraAspectRatio + 0.5);
lp.width = w;
lp.topMargin = (h - lp.height)/2;
}
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
setLayoutParams(lp);
requestLayout();
}
To emphasize the main idea, I did not include error handling, and I did not show here how the camera is actually started with a secondary Looper.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…