使SurfaceView大于屏幕(将相机预览适合大于显示器的SurfaceView)

我有一个自定义相机应用程序,我想要任何预览尺寸以全屏模式显示它而不拉伸相机预览图像。 为此,我需要使surfaceView比屏幕大,以保持纵横比,因此实际上用户将看到比实际捕获的相机少。

出于某种原因,我无法使SurfaceView大于屏幕尺寸。

到目前为止我尝试了什么:

  • 调整surfaceChanged方法中的相机预览

  • onMeasure方法上调整相机预览的大小

  • onLayout 方法中resize
  • FLAG_LAYOUT_NO_LIMITS添加到活动 – 信息
  • 为表面视图添加androidclipChildren – info
  • 在xml中设置宽度: android:layout_width="852px"
  • getWindow().setLayout(852, 1280); 在活动中

但是没有任何成功 – 每次的行为都是一样的:它看起来没有1秒钟,之后它会被拉长。

这是代码:

 public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final int CAMERA_ROTATE_ANGLE = 90; private SurfaceHolder cameraHolder; private Camera androidHardCamera; private Context context; public CameraPreview(Context context) { super(context); this.context = context; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. cameraHolder = getHolder(); cameraHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 cameraHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera camera) { this.androidHardCamera = camera; if (androidHardCamera != null) { requestLayout(); } } @Override public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { if (androidHardCamera != null) { androidHardCamera.stopPreview();//this is needed for devices with API level < 14 (from doc.: Starting // from API level 14, this method, aka setDisplayOrientation, can be called when preview is active.) androidHardCamera.setDisplayOrientation(CAMERA_ROTATE_ANGLE);//force the preview Display Orientation // to Portrait (rotate camera orientation/display to portrait) //holder.setFixedSize(852, 1280); androidHardCamera.setPreviewDisplay(holder); androidHardCamera.startPreview(); } } catch (IOException e) { } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 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 (cameraHolder.getSurface() == null) { // preview surface does not exist return; } // stop preview before making changes try { androidHardCamera.stopPreview(); } catch (Exception e) { // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) this.getLayoutParams(); layoutParams.height = 1280; layoutParams.width = 852; this.setLayoutParams(layoutParams); //cameraHolder.setFixedSize(852, 1280); requestLayout(); // start preview with new settings try { androidHardCamera.setPreviewDisplay(cameraHolder); androidHardCamera.startPreview(); } catch (Exception e) { } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } // @Override // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // // super.onMeasure(widthMeasureSpec, heightMeasureSpec); //To change body of overridden methods use File | Settings | File Templates. // //super.onMeasure(852, 1280); // setMeasuredDimension(852, 1280); // } } public class MyActivity extends Activity{ private Camera camera; private CameraPreview previewCamera; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); setContentView(R.layout.camera_screen); previewCamera = new CameraPreview(this); FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview); preview.addView(previewCamera); //getWindow().setLayout(852, 1280); } @Override protected void onResume() { // Create an instance of Camera camera = getCameraInstance(1); if (camera == null) { Toast.makeText(this, "Camera in use!", Toast.LENGTH_LONG).show(); } else { previewCamera.setCamera(camera); camera.stopPreview(); Camera.Parameters p = camera.getParameters(); p.setPreviewSize(176, 144); // p.setPreviewSize(480, 800); camera.setParameters(p); startPreviewCamera(); } super.onResume(); } @Override protected void onPause() { releaseCameraAndPreview(); super.onPause(); } public Camera getCameraInstance(int cameraInstance) { Camera c = null; try { c = Camera.open(cameraInstance); } catch (Exception e) { // Camera is not available (in use or does not exist) System.out.println("exception: " + e); } return c; } public void startPreviewCamera() { //Force the preview Display Orientation to Portrait (rotate camera orientation/display to portrait) camera.setDisplayOrientation(90); camera.startPreview(); } public void releaseCameraAndPreview() { if (camera != null) { camera.stopPreview(); // updating the preview surface camera.setPreviewCallback(null); // camera.lock(); //if we don't lock the camera, release() will fail on some devices camera.release(); camera = null; } } }      

这是整个项目: https : //www.dropbox.com/sh/96jih9kw5zmmnzy/z7VX16T30M

我正在测试S3设备。 在S2设备上好像很好……我只是不知道该怎么做才能解决这个问题…

更新1

例如,索尼Xperia的屏幕显示为480/854。我可以使用的预览尺寸之一是176/144。

为了显示全屏尺寸,我需要预览相机尺寸为698/854 – 但我不知道如何设置此值和位置。

下面的代码不起作用…相机预览被拉伸/拉长。

 import android.app.Activity; import android.graphics.Point; import android.hardware.Camera; import android.os.Bundle; import android.view.Display; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; public class CameraPreview extends Activity implements Preview.PreviewListener { private Preview mPreview; private Camera mCamera; FrameLayout preview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(R.layout.main); // Create our Preview view and set it as the content of our activity. mPreview = new Preview(this); preview = (FrameLayout) findViewById(R.id.surface_camera); preview.addView(mPreview); Display display = getWindowManager().getDefaultDisplay(); getDisplaySize(display); } private static Point getDisplaySize(final Display display) { final Point point = new Point(); try { display.getSize(point); } catch (java.lang.NoSuchMethodError ignore) { point.x = display.getWidth(); point.y = display.getHeight(); } System.out.println("============: Screen " + point.x + "/" + point.y); return point; } @Override protected void onResume() { super.onResume(); mCamera = Camera.open(1); mPreview.setCamera(mCamera, this); } @Override protected void onPause() { super.onPause(); if (mCamera != null) { mPreview.setCamera(null, null); mCamera.release(); mCamera = null; } } @Override public void onSurfaceChanged() { LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) preview.getLayoutParams(); params.setMargins(0, -218, 0, 0); preview.setLayoutParams(new FrameLayout.LayoutParams(480, 854)); preview.setLayoutParams(params); preview.setVisibility(View.VISIBLE); } } 

 import android.content.Context; import android.hardware.Camera; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.io.IOException; import java.util.List; class Preview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; private PreviewListener listener; public static interface PreviewListener { void onSurfaceChanged(); } Preview(Context context) { super(context); mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera camera, PreviewListener listener) { this.listener = listener; mCamera = camera; if (mCamera != null) { List mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes(); for (Camera.Size s : mSupportedPreviewSizes) { System.out.println("============: " + s.width + "/" + s.height); } requestLayout(); } } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, acquire the camera and tell it where // to draw. try { if (mCamera != null) { mCamera.setPreviewDisplay(holder); } } catch (IOException exception) { Log.e("Error: ", "IOException caused by setPreviewDisplay()", exception); } } public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return, so stop the preview. if (mCamera != null) { mCamera.stopPreview(); } } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mCamera.stopPreview(); // pe Xpedia daca nu pui asta crapa la setDisplayOrientation // Now that the size is known, set up the camera parameters and beginthe preview. Camera.Parameters parameters = mCamera.getParameters(); mCamera.setDisplayOrientation(90); parameters.setPreviewSize(176, 144); requestLayout(); mCamera.setParameters(parameters); mCamera.startPreview(); } // @Override // protected void onSizeChanged(\int w, int h, int oldw, int oldh) { // super.onSizeChanged(w, h, oldw, oldh); // //setLayoutParams(new LayoutParams((int)RATIO * w, w)); // // FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); // setLayoutParams(new FrameLayout.LayoutParams(960, 1280)); // params.setMargins(0, -120, 0,0); // setLayoutParams(params); // // //preview.setVisibility(View.VISIBLE); // } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //To change body of overridden methods use File | Settings | File Templates. // FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); // setLayoutParams(new FrameLayout.LayoutParams(698, 854)); // params.setMargins(0, -218, 0,0); // setLayoutParams(params); } //https://stackoverflow.com/questions/11853297/change-size-of-android-custom-surfaceview @Override public void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { //setLayoutParams(); listener.onSurfaceChanged(); //(this).layout(0, 0, viewWidth , viewHeight); } } } 

这是一个类测试,它根据显示屏大小和相机预览大小计算正确的表面视图大小:

 public class Test { /** * Determine proper width to be used for surface view in order to not stretch the camera live preview. */ public static void main(String[] args) { // camera preview size: int surfaceViewWidth = 176; int surfaceViewHeight = 144; int holder; if (surfaceViewWidth > surfaceViewHeight) { holder = surfaceViewWidth; surfaceViewWidth = surfaceViewHeight; surfaceViewHeight = holder; } //device screen display sizes: int width = 480; int height = 854; double sc1 = (double) width / surfaceViewWidth; double sc2 = (double) height / surfaceViewHeight; double rez; if (sc1 > sc2) { rez = sc1; } else { rez = sc2; } System.out.println("Width/height: " + (int) (surfaceViewWidth * rez) + "/" + (int) (surfaceViewHeight * rez)); // size of the preview size we need to set System.out.println(((int) (surfaceViewWidth * rez))-width); // difference between preview size and device screen size = whit how much is bigger the preview size than screen size } } 

首先,删除崩溃源:在onResume中调用startPreviewCamera。 相机预览应在SurfaceHolder.Callback方法中启动。

然后您应该知道,您只能将预览大小设置为Camera.Parameters.getSupportedPreviewSizes报告的大小。 这些尺寸很可能小于或等于设备的屏幕尺寸。

然后你只需打电话

 Camera.Parameters p = camera.getParameters(); p.setPreviewSize(w, h); // one of supported sizes camera.setParameters(p); 

然后预览表面将具有该大小(可能旋转和w / h交换)。 在绘制时,此表面将由Android重新调整为您的CameraPreview视图的大小,因此设置CameraPreview的大小也很重要。

您只需拨打电话即可设置CameraPreview的固定大小

 previewCamera.setLayoutParams(new FrameLayout.LayoutParams(w, h)); 

简而言之,您可以在Camera.setParameters中设置请求的预览大小,并根据需要调整预览视图的大小,可能与预览大小相同,这也是您的要求。 然后,您的预览视图可能等于屏幕尺寸或更小(假设相机不提供​​比屏幕更大的预览)。 如果相机提供比屏幕更大的预览,您仍然可以调用preview.setX,preview.setY来移动它。