一、任意大小、任意位置


1) 代码

public void init() {
    
    // FrameLayout
    ViewGroup.LayoutParams framelayout_params =
        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                   ViewGroup.LayoutParams.MATCH_PARENT);
    mFrameLayout = new FrameLayout(this);
    mFrameLayout.setLayoutParams(framelayout_params);
 
    // Cocos2dxEditText layout
    ViewGroup.LayoutParams edittext_layout_params =
        new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                   ViewGroup.LayoutParams.WRAP_CONTENT);
    Cocos2dxEditText edittext = new Cocos2dxEditText(this);
    edittext.setLayoutParams(edittext_layout_params);
 
    // ...add to FrameLayout
    mFrameLayout.addView(edittext);
 
    //
    // Added by Myarrow on 2014-10-28 start
    // Create SurfaceView for MediaPlayer
    mMediaPlayView = new SurfaceView(this);  
    //mMediaPlayView.setBackgroundResource(cn.xx.yy.R.drawable.videostart);
    mFrameLayout.addView(mMediaPlayView);  		
 
    // Create SurfaceView for camera preview
    mCameraView = new SurfaceView(this);
    mCameraView.setZOrderOnTop(true);
    FrameLayout.LayoutParams cameraFL = new FrameLayout.LayoutParams(320, 240,Gravity.TOP); // set size
    cameraFL.setMargins(900, 50, 0, 0);  // set position
    mCameraView.setLayoutParams(cameraFL);
    mFrameLayout.addView(mCameraView);
    // Added by MyArrow end
 
    // Cocos2dxGLSurfaceView
    this.mGLSurfaceView = this.onCreateView();
    //this.mGLSurfaceView.setBackgroundColor(Color.BLUE);
 
    // ...add to FrameLayout
    mFrameLayout.addView(this.mGLSurfaceView);
 
    // Switch to supported OpenGL (ARGB888) mode on emulator
    if (isAndroidEmulator())
       this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
 
    // Added by MyArrow on 2014-10-28  
    this.mGLSurfaceView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);  
    this.mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);  
    this.mGLSurfaceView.setZOrderOnTop(true);  
    // Added by MyArrow end
 
    this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
    this.mGLSurfaceView.setCocos2dxEditText(edittext);
 
    // Set framelayout as the content view
    setContentView(mFrameLayout);
}

2) 效果图

Android开发利用SurfaceView预览画面 surfaceview设置大小_2d

二、相机预览保持宽高比与实际屏显一致

实现方式一:

try {
Camera.Parameters params = mCamera.getParameters();
Point realScreenSize = DisplayUtil.getRealScreenMetrics(CameraActivity.this);
Point previewSize = DisplayUtil.getBestCameraResolution(params, realScreenSize);
params.setPreviewSize(previewSize.x, previewSize.y);//获得摄像区域的大小
try {
   mCamera.setParameters(params);//把上面的设置 赋给摄像头
} catch (Exception e) {
   e.printStackTrace();
}
mCamera.setPreviewDisplay(surfaceHolder);//把摄像头获得画面显示在SurfaceView控件里面
mCamera.startPreview();//开始预览
       setCameraDisplayOrientation(CameraActivity.this, mCurrentCamIndex, mCamera);//调节预览方向,参数mCurrentCamIndex是相机的id
} catch (Exception e) {
   e.printStackTrace();
}

基于屏幕的宽度和包含底部虚拟按钮的屏幕高度的比值,然后从camera获取其参数——Camera.Parameters,利用camera参数获取相机支持的预览尺寸集,即:getSupportedPreviewSizes()(返回值为List<Camera.Size>),从中选择出宽高比与屏幕宽高比最接近的宽高比,最后将此宽高比对应的Camera.Size设置为预览尺寸,然后再将设置好的params重新设置给camera。

以下是根据屏幕实际尺寸的宽高比选出相机支持的预览尺寸中与之最为接近的尺寸:

/**
 * 获取最佳预览大小
 *
 * @param parameters       相机参数
 * @param screenResolution 屏幕宽高
 * @return
 */
public static Point getBestCameraResolution(Camera.Parameters parameters, Point screenResolution) {
   float tmp = 0f;
   float mindiff = 100f;
   float x_d_y = (float) screenResolution.x / (float) screenResolution.y;
   Camera.Size best = null;
   List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
   for (Camera.Size s : supportedPreviewSizes) {
      tmp = Math.abs(((float) s.height / (float) s.width) - x_d_y);//通过调试运行可以发现,这里的宽是大于高的,也就是横置屏幕的效果。所以我们用高/宽来作为实际竖屏时的宽高比。
      if (tmp < mindiff) {
         mindiff = tmp;
         best = s;
      }
   }
   return new Point(best.width, best.height);
}

如果不是从camera的预览尺寸集中选取尺寸的话,在相机setParameters时会报错:java.Lang.RuntimeException, setParameters failed…
一般出现此错误的原因是:设置的预览尺寸previewSize或设置的输出图片尺寸pictureSize等不合理(底层不支持)。
比如我的手机分辨率是1920*1080但经一般的测量方式计算后,屏幕的宽是1080,高是1800,高度上减少是因为底部的虚拟按钮的高度没被计算进来,但我所测量的宽和高是实际的宽高,包含状态栏和底部虚拟按键(底部导航栏)。以下是获取实际宽高代码:

public static Point getRealScreenMetrics(Activity context) {
   int realWidth = 0, realHeight = 0;
   try {
      Display display = context.getWindowManager().getDefaultDisplay();
      DisplayMetrics metrics = new DisplayMetrics();
      display.getMetrics(metrics);
      if (android.os.Build.VERSION.SDK_INT >= 17) {
         Point size = new Point();
         display.getRealSize(size);
         realWidth = size.x;
         realHeight = size.y;
      } else if (android.os.Build.VERSION.SDK_INT < 17
            && android.os.Build.VERSION.SDK_INT >= 14) {
         Method mGetRawH = Display.class.getMethod("getRawHeight");
         Method mGetRawW = Display.class.getMethod("getRawWidth");
         realWidth = (Integer) mGetRawW.invoke(display);
         realHeight = (Integer) mGetRawH.invoke(display);
      } else {
         realWidth = metrics.widthPixels;
         realHeight = metrics.heightPixels;
      }

   } catch (Exception e) {
      e.printStackTrace();
   }
   return new Point(realWidth, realHeight);
}

最后一定要记得,调整预览方向为portrait,即竖屏。以下是竖屏调整代码:

//根据横竖屏自动调节preview方向,Starting from API level 14, this method can be called when preview is active.
private static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
   Camera.CameraInfo info = new Camera.CameraInfo();
   Camera.getCameraInfo(cameraId, info);
   int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();

   //degrees  the angle that the picture will be rotated clockwise. Valid values are 0, 90, 180, and 270.
   //The starting position is 0 (landscape).
   int degrees = 0;
   switch (rotation) {
      case Surface.ROTATION_0:
         degrees = 0;
         break;
      case Surface.ROTATION_90:
         degrees = 90;
         break;
      case Surface.ROTATION_180:
         degrees = 180;
         break;
      case Surface.ROTATION_270:
         degrees = 270;
         break;
   }
   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;
   }
   camera.setDisplayOrientation(result);
}

实现方式二(推荐,因为简洁):
借助CamcorderProfile类:

public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
         try {
            //设置手机前置摄像头预览的
       CamcorderProfile profile= (CamcorderProfile.get(mCurrentCamIndex, CamcorderProfile.QUALITY_HIGH));
            Camera.Parameters params = mCamera.getParameters();
            params.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
            try {
               mCamera.setParameters(params);//把上面的设置 赋给摄像头
            } catch (Exception e) {
               e.printStackTrace();
            }
            mCamera.setPreviewDisplay(surfaceHolder);//把摄像头获得画面显示在SurfaceView控件里面
       mCamera.startPreview();//开始预览
       previewing = true;
            setCameraDisplayOrientation(CameraActivity.this, mCurrentCamIndex, mCamera);//调节预览方向
         } catch (Exception e) {
            e.printStackTrace();
         }
}