我为Android应用程序制作了像“SnapChat”这样的自定义相机,但相机预览在很少的设备上展开,例如(Moto g第二代,一个+一个)但没有(三星s3,三星s4)。我使用了以下参考Camera display / preview in full screen does not maintain aspect ratio - image is skewed, stretched in order to fit on the screen。但这对我100%没有帮助。我正在分享屏幕。
三星Moto G第二代的延伸图像是。
未拉伸的三星S3图像高于
private void setPreviewLayout() {
if (null == mCamera) {
return;
}
Camera.Parameters parameters = null;
Camera.Size size = null;
try {
int screenWidth = (int) getResources().getDisplayMetrics().widthPixels;
int screenHeight = (int) getResources().getDisplayMetrics().heightPixels;
parameters = mCamera.getParameters();
size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);
if (size != null) {
parameters.setPreviewSize(size.width, size.height);
}
parameters.setPictureSize(screenHeight, screenWidth);
;
mCamera.setParameters(parameters);
if (on && currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
} else {
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
parameters.setExposureCompensation(0);
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setJpegQuality(100);
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
mCamera.setParameters(parameters);
/*
* camera.setPreviewDisplay(surfaceHolder); camera.startPreview();
*/
} catch (Exception e) {
e.printStackTrace();
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
答案 0 :(得分:16)
使用getOptimalPreviewSize()
很重要,但它并不能解决所有布局中所有设备的所有拉伸问题。您必须准备好稍微裁剪预览,以便预览填满屏幕而不会失真。
有不同的技术可以强制表面尺寸与实际屏幕尺寸不同,但我发现这个最简单:
我将CameraView
添加到我的布局:
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();
}
为了强调主要想法,我没有包含错误处理,我没有在这里显示相机is actually started with a secondary Looper。
答案 1 :(得分:1)
您需要覆盖SurfaceView类的onMeasure方法以获取surfaceview的宽度和高度
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), width, height);
}
}
然后将您获得的预览尺寸设置为相机参数预览尺寸。
cameraParameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(cameraParameters);
getSuggestedMinimumHeight(),getSuggestedMinimumWidth()是View类的方法
希望这有帮助!!!
答案 2 :(得分:0)
试试这是有效的......
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "Camera Preview";
private static final double PREVIEW_SIZE_FACTOR = 3.00;
private SurfaceHolder mHolder = null;
private Camera mCamera = null;
Camera.Parameters _parameters=null;
@SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
Camera.Parameters parameters = mCamera.getParameters();
final Size mPreviewSize = getOptimalSize();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
//startContinuousAutoFocus();
} catch (Exception e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
@SuppressLint("NewApi")
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// set Camera parameters
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void startPreview(){
try{
mCamera.startPreview();
}catch (Exception e) {
}
}
private Size getOptimalSize() {
Camera.Size result = null;
final Camera.Parameters parameters = mCamera.getParameters();
Log.i(CameraPreview.class.getSimpleName(), "window width: " + getWidth() + ", height: " + getHeight());
for (final Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= getWidth() * PREVIEW_SIZE_FACTOR && size.height <= getHeight() * PREVIEW_SIZE_FACTOR) {
if (result == null) {
result = size;
} else {
final int resultArea = result.width * result.height;
final int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
if (result == null) {
result = parameters.getSupportedPreviewSizes().get(0);
}
Log.i(CameraPreview.class.getSimpleName(), "Using PreviewSize: " + result.width + " x " + result.height);
return result;
}
}