我一直在努力在Android上制作自定义相机活动,但在旋转相机时,表面视图的纵横比会变得混乱。
在我的oncreate活动中,我设置了framelayout,它保存了显示摄像机参数的表面视图。
//FrameLayout that will hold the camera preview
FrameLayout previewHolder = (FrameLayout) findViewById(R.id.camerapreview);
//Setting camera's preview size to the best preview size
Size optimalSize = null;
camera = getCameraInstance();
double aspectRatio = 0;
if(camera != null){
//Setting the camera's aspect ratio
Camera.Parameters parameters = camera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
optimalSize = CameraPreview.getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
aspectRatio = (float)optimalSize.width/optimalSize.height;
}
if(optimalSize!= null){
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
previewHolder.setLayoutParams(params);
LayoutParams surfaceParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int)(getResources().getDisplayMetrics().widthPixels*aspectRatio));
cameraPreview.setLayoutParams(surfaceParams);
}
cameraPreview.setCamera(camera);
//Adding the preview to the holder
previewHolder.addView(cameraPreview);
然后,在Surface视图中,我设置要显示的摄像机参数
public void setCamera(Camera camera) {
if (mCamera == camera) { return; }
mCamera = camera;
if (mCamera != null) {
requestLayout();
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
if(mCamera != null){
//Setting the camera's aspect ratio
Camera.Parameters parameters = mCamera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
mCamera.setParameters(parameters);
}
/*
Important: Call startPreview() to start updating the preview surface. Preview must
be started before you can take a picture.
*/
mCamera.startPreview();
}
}
你可以看到,当手机旋转时,乐高男人会变得更高更瘦;
如何确保相机视图的宽高比正确?
答案 0 :(得分:157)
我正在使用这种方法 - &gt;基于API演示来获取我的预览尺寸:
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;
}
如您所见,您必须输入屏幕的宽度和高度。此方法将根据这些值计算屏幕比率,然后从supportedPreviewSizes列表中选择最适合您的屏幕比率。使用
获取您的supportedPreviewSize列表,其中Camera对象不为nullmSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
然后在onMeasure上你可以获得最佳的previewSize:
@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(mSupportedPreviewSizes, width, height);
}
}
然后(在我的SurfaceChanged方法的代码中,就像我说我使用的是CameraActivity代码的API Demos结构,你可以在Eclipse中生成它):
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.startPreview();
还有一个暗示,因为我和你一样做了几乎相同的应用程序。 Camera Activity的好习惯是隐藏StatusBar。 Instagram等应用程序正在这样做。它会降低屏幕高度值并更改比率值。某些设备上可能会出现奇怪的预览尺寸(您的SurfaceView会被剪掉一点)
要回答您的问题,如何检查您的预览比例是否正确?然后获取您在以下位置设置的参数的高度和宽度:
mCamera.setParameters(parameters);
您的设定比率等于身高/宽度。如果您希望相机在屏幕上看起来不错,那么您设置为相机的参数的高宽比必须与屏幕的高度(减去状态栏)/宽度比相同。
答案 1 :(得分:140)
F1Sher的解决方案很好但有时不起作用。特别是,当您的surfaceView不覆盖整个屏幕时。在这种情况下,您需要覆盖onMeasure()方法。 我在这里复制了我的代码供您参考。
因为我根据宽度测量了surfaceView,所以我在屏幕的末尾有一点白色的间隙,我按设计填充它。如果您保持高度并通过将其乘以比率来增加宽度,则可以解决此问题。但是,它会稍微挤压surfaceView。
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
public CameraPreview(Context context, Camera camera) {
super(context);
mContext = context;
mCamera = camera;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
for(Camera.Size str: mSupportedPreviewSizes)
Log.e(TAG, str.width + "/" + str.height);
// 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) {
// empty. surfaceChanged will take care of stuff
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged => w=" + w + ", h=" + 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
// start preview with new settings
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
if (mPreviewSize!=null) {
float ratio;
if(mPreviewSize.height >= mPreviewSize.width)
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
else
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
// One of these methods should be used, second method squishes preview slightly
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
}
}
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.height / size.width;
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;
}
}
答案 2 :(得分:23)
注意:我的解决方案是对HESAM解决方案的延续:https://stackoverflow.com/a/22758359/1718734
我所说的:Hesam说有些白色空间可能出现在某些手机上,如下:
Hesam建议采用第二种解决方案,但是要求预览。在某些设备上,它会严重扭曲。
那么我们如何解决这个问题呢。它很简单......通过乘以纵横比直到它填满屏幕。我注意到,一些流行的应用程序,如Snapchat,WhatsApp等以同样的方式工作。
您只需将此添加到onMeasure方法:
float camHeight = (int) (width * ratio);
float newCamHeight;
float newHeightRatio;
if (camHeight < height) {
newHeightRatio = (float) height / (float) mPreviewSize.height;
newCamHeight = (newHeightRatio * camHeight);
Log.e(TAG, camHeight + " " + height + " " + mPreviewSize.height + " " + newHeightRatio + " " + newCamHeight);
setMeasuredDimension((int) (width * newHeightRatio), (int) newCamHeight);
Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | H_ratio - " + newHeightRatio + " | A_width - " + (width * newHeightRatio) + " | A_height - " + newCamHeight);
} else {
newCamHeight = camHeight;
setMeasuredDimension(width, (int) newCamHeight);
Log.e(TAG, mPreviewSize.width + " | " + mPreviewSize.height + " | ratio - " + ratio + " | A_width - " + (width) + " | A_height - " + newCamHeight);
}
这将计算屏幕高度并获得屏幕高度与mPreviewSize高度的比率。然后它将相机的宽度和高度乘以新的高度比,并相应地设置测量的尺寸。
接下来你知道了,你最终得到了这个:D
这也适用于前置摄像头。我相信这是解决这个问题的最好方法。现在我的应用程序唯一剩下的就是在点击“Capture”时保存预览。但是,是的,就是这样。
答案 3 :(得分:10)
好的,所以我认为对于一般的相机预览拉伸问题没有足够的答案。或者至少我找不到一个。我的应用程序也遭遇了这种拉伸综合症,我花了一段时间才将这个门户和互联网上所有用户答案的解决方案拼凑起来。
我试过@Hesam的solution,但它不起作用,让我的相机预览严重失真。
首先,我展示了我的解决方案的代码(代码的重要部分)然后我解释了为什么我采取了这些步骤。有改进性能的空间。
主要活动xml布局:
<RelativeLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/camera_preview"
android:layout_centerInParent="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
相机预览:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder prHolder;
private Camera prCamera;
public List<Camera.Size> prSupportedPreviewSizes;
private Camera.Size prPreviewSize;
@SuppressWarnings("deprecation")
public YoCameraPreview(Context context, Camera camera) {
super(context);
prCamera = camera;
prSupportedPreviewSizes = prCamera.getParameters().getSupportedPreviewSizes();
prHolder = getHolder();
prHolder.addCallback(this);
prHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
prCamera.setPreviewDisplay(holder);
prCamera.startPreview();
} catch (IOException e) {
Log.d("Yologram", "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (prHolder.getSurface() == null){
return;
}
try {
prCamera.stopPreview();
} catch (Exception e){
}
try {
Camera.Parameters parameters = prCamera.getParameters();
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
parameters.setPreviewSize(prPreviewSize.width, prPreviewSize.height);
prCamera.setParameters(parameters);
prCamera.setPreviewDisplay(prHolder);
prCamera.startPreview();
} catch (Exception e){
Log.d("Yologram", "Error starting camera preview: " + e.getMessage());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
if (prSupportedPreviewSizes != null) {
prPreviewSize =
getOptimalPreviewSize(prSupportedPreviewSizes, width, height);
}
}
public 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;
}
}
主要活动:
public class MainActivity extends Activity {
...
@SuppressLint("NewApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
maCamera = getCameraInstance();
maLayoutPreview = (FrameLayout) findViewById(R.id.camera_preview);
maPreview = new CameraPreview(this, maCamera);
Point displayDim = getDisplayWH();
Point layoutPreviewDim = calcCamPrevDimensions(displayDim,
maPreview.getOptimalPreviewSize(maPreview.prSupportedPreviewSizes,
displayDim.x, displayDim.y));
if (layoutPreviewDim != null) {
RelativeLayout.LayoutParams layoutPreviewParams =
(RelativeLayout.LayoutParams) maLayoutPreview.getLayoutParams();
layoutPreviewParams.width = layoutPreviewDim.x;
layoutPreviewParams.height = layoutPreviewDim.y;
layoutPreviewParams.addRule(RelativeLayout.CENTER_IN_PARENT);
maLayoutPreview.setLayoutParams(layoutPreviewParams);
}
maLayoutPreview.addView(maPreview);
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
private Point getDisplayWH() {
Display display = this.getWindowManager().getDefaultDisplay();
Point displayWH = new Point();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
display.getSize(displayWH);
return displayWH;
}
displayWH.set(display.getWidth(), display.getHeight());
return displayWH;
}
private Point calcCamPrevDimensions(Point disDim, Camera.Size camDim) {
Point displayDim = disDim;
Camera.Size cameraDim = camDim;
double widthRatio = (double) displayDim.x / cameraDim.width;
double heightRatio = (double) displayDim.y / cameraDim.height;
// use ">" to zoom preview full screen
if (widthRatio < heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = displayDim.x;
calcDimensions.y = (displayDim.x * cameraDim.height) / cameraDim.width;
return calcDimensions;
}
// use "<" to zoom preview full screen
if (widthRatio > heightRatio) {
Point calcDimensions = new Point();
calcDimensions.x = (displayDim.y * cameraDim.width) / cameraDim.height;
calcDimensions.y = displayDim.y;
return calcDimensions;
}
return null;
}
}
我的评论:
所有这一切的重点是,尽管您在getOptimalPreviewSize()
中计算最佳相机尺寸,但您只选择最近比率以适合您的屏幕。因此,除非比率完全相同,否则预览将会延伸。
为什么会伸展?因为FrameLayout相机预览在 layout.xml 中设置为 match_parent 的宽度和高度。这就是预览将全屏延伸的原因。
需要做的是设置相机预览布局宽度和高度以匹配所选的相机尺寸比,因此预览会保持其宽高比而不会扭曲。
我尝试使用CameraPreview
类来完成所有计算和布局更改,但我无法理解。我尝试应用this solution,但SurfaceView
无法识别getChildCount ()
或getChildAt (int index)
。我想,我最终通过引用maLayoutPreview
得到了它,但它行为不端并将设定比率应用到我的整个应用程序中,并且在拍完第一张照片后就这样做了。所以我放手将布局修改移到MainActivity
。
在CameraPreview
我将prSupportedPreviewSizes
和getOptimalPreviewSize()
更改为公开,以便我可以在MainActivity
中使用它。然后我需要显示尺寸(减去导航/状态栏,如果有)并选择最佳相机尺寸。我试图获取RelativeLayout(或FrameLayout)大小而不是显示大小,但它返回零值。 This solution对我不起作用。布局在onWindowFocusChanged
之后得到它的值(在日志中检查)。
所以我有计算布局尺寸的方法,以匹配所选摄像机尺寸的宽高比。现在,您只需设置相机预览版面的LayoutParams
即可。更改宽度,高度并将其居中放在父级中。
如何计算预览尺寸有两种选择。您希望它在侧面或顶部/底部的黑条(如果windowBackground设置为null)适合屏幕。或者您希望预览缩放到全屏。我在calcCamPrevDimensions()
中留下了有关更多信息的评论。
答案 4 :(得分:7)
嗨,这里的getOptimalPreview()并没有为我工作,所以我想分享我的版本:
File file = new File("new.svg");
FileWriter fWriter = new FileWriter(file);
XmlWriter.writeXml(svg, fWriter, false);
// Most crucial part, It wasn't working just because of flush
fWriter.close();
答案 5 :(得分:2)
为了让这个帖子更完整,我正在添加我的答案版本:
我想要实现的目标: 表面视图不应该拉伸,它应该覆盖整个屏幕,而且,我的应用程序中只有横向模式。
解决方案:
解决方案是F1sher解决方案的极小扩展:
=&GT;第一步是整合F1sher的解决方案。
=&GT;现在,当表面视图没有覆盖整个屏幕时,F1sher的解决方案可能会出现一个场景。解决方案是使表面视图大于屏幕尺寸,使其覆盖整个屏幕,为此:
size = getOptimalPreviewSize(mCamera.getParameters().getSupportedPreviewSizes(), screenWidth, screenHeight);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
double screenRatio = (double) screenHeight / screenWidth;
double previewRatio = (double) size.height / size.width;
if (previewRatio > screenRatio) /*if preview ratio is greater than screen ratio then we will have to recalculate the surface height while keeping the surface width equal to the screen width*/
{
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
params1.addRule(RelativeLayout.CENTER_IN_PARENT);
flPreview.setLayoutParams(params1);
flPreview.setClipChildren(false);
LayoutParams surfaceParams = new LayoutParams(screenWidth, (int) (screenWidth * previewRatio));
surfaceParams.gravity = Gravity.CENTER;
mPreview.setLayoutParams(surfaceParams);
}
else /*if preview ratio is smaller than screen ratio then we will have to recalculate the surface width while keeping the surface height equal to the screen height*/
{
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
params1.addRule(RelativeLayout.CENTER_IN_PARENT);
flPreview.setLayoutParams(params1);
flPreview.setClipChildren(false);
LayoutParams surfaceParams = new LayoutParams((int) ((double) screenHeight / previewRatio), screenHeight);
surfaceParams.gravity = Gravity.CENTER;
mPreview.setLayoutParams(surfaceParams);
}
flPreview.addView(mPreview);
/* The TopMost layout used is the RelativeLayout, flPreview is the FrameLayout in which Surface View is added, mPreview is an instance of a class which extends SurfaceView */
答案 6 :(得分:1)
我弄清楚问题是什么 - 它是方向更改。如果您将相机方向更改为90或270度,而不是交换宽度和高度所支持的尺寸,一切都可以。
表面视图也应位于框架布局中并具有中心重力。
这是C#(Xamarin)的例子:
public void SurfaceChanged(ISurfaceHolder holder, Android.Graphics.Format format, int width, int height)
{
_camera.StopPreview();
// find best supported preview size
var parameters = _camera.GetParameters();
var supportedSizes = parameters.SupportedPreviewSizes;
var bestPreviewSize = supportedSizes
.Select(x => new { Width = x.Height, Height = x.Width, Original = x }) // HACK swap height and width because of changed orientation to 90 degrees
.OrderBy(x => Math.Pow(Math.Abs(x.Width - width), 3) + Math.Pow(Math.Abs(x.Height - height), 2))
.First();
if (height == bestPreviewSize.Height && width == bestPreviewSize.Width)
{
// start preview if best supported preview size equals current surface view size
parameters.SetPreviewSize(bestPreviewSize.Original.Width, bestPreviewSize.Original.Height);
_camera.SetParameters(parameters);
_camera.StartPreview();
}
else
{
// if not than change surface view size to best supported (SurfaceChanged will be called once again)
var layoutParameters = _surfaceView.LayoutParameters;
layoutParameters.Width = bestPreviewSize.Width;
layoutParameters.Height = bestPreviewSize.Height;
_surfaceView.LayoutParameters = layoutParameters;
}
}
请注意相机参数应设置为原始尺寸(未交换),并且应交换表面视图尺寸。
答案 7 :(得分:1)
我尝试了上面的所有解决方案,但它们都不适合我。最后我自己解决了,发现其实很容易。有两点需要注意。
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
此previewSize必须是相机支持的分辨率之一,可以如下所示:
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
通常rawSupportedSize中的一个等于设备分辨率。
其次,将SurfaceView放在FrameLayout中,并在surfaceChanged方法中设置表面布局高度和宽度,如上所述
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) surfaceView.getLayoutParams();
layoutParams.height = cameraResolution.x;
layoutParams.width = cameraResolution.y;
好的,事情已经完成,希望这可以帮到你。
答案 8 :(得分:1)
@Hesam的answer是正确的,CameraPreview
将在所有纵向设备上都可以使用,但是如果设备处于横向模式或多窗口模式下,则此代码可以正常工作,只需替换onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
setMeasuredDimension(width, height);
int rotation = ((Activity) mContext).getWindowManager().getDefaultDisplay().getRotation();
if ((Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) {//portrait
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, height, width);
} else
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);//landscape
if (mPreviewSize == null) return;
float ratio;
if (mPreviewSize.height >= mPreviewSize.width) {
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
} else ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && ((Activity) mContext).isInMultiWindowMode()) {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ||
!(Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) {
setMeasuredDimension(width, (int) (width / ratio));
} else {
setMeasuredDimension((int) (height / ratio), height);
}
} else {
if ((Surface.ROTATION_0 == rotation || Surface.ROTATION_180 == rotation)) {
Log.e("---", "onMeasure: " + height + " - " + width * ratio);
//2264 - 2400.0 pix c -- yes
//2240 - 2560.0 samsung -- yes
//1582 - 1440.0 pix 2 -- no
//1864 - 2048.0 sam tab -- yes
//848 - 789.4737 iball -- no
//1640 - 1600.0 nexus 7 -- no
//1093 - 1066.6667 lenovo -- no
//if width * ratio is > height, need to minus toolbar height
if ((width * ratio) < height)
setMeasuredDimension(width, (int) (width * ratio));
else
setMeasuredDimension(width, (int) (width * ratio) - toolbarHeight);
} else {
setMeasuredDimension((int) (height * ratio), height);
}
}
requestLayout();
}
答案 9 :(得分:1)
这里很重要的一点要理解,SurfaceView 的大小必须与相机参数的大小相同,这意味着它们具有相同的纵横比,那么 Stretch 效果就会消失。
您必须使用 params.getSupportedPreviewSizes() 获得正确支持的相机预览尺寸,选择其中之一,然后将 SurfaceView 及其支架更改为该尺寸。
答案 10 :(得分:0)
我的要求是相机预览需要全屏并保持纵横比。 Hesam和Yoosuf的解决方案很棒,但出于某种原因我确实看到了高变焦问题。
想法是一样的,将预览容器中心放在父级中,并根据纵横比增加宽度或高度,直到它可以覆盖整个屏幕。
需要注意的一点是预览尺寸是横向的,因为我们设置了显示方向。
camera.setDisplayOrientation(90);
我们将SurfaceView视图添加到的容器:
<RelativeLayout
android:id="@+id/camera_preview_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"/>
将预览添加到活动中心位于父级的容器中。
this.cameraPreview = new CameraPreview(this, camera);
cameraPreviewContainer.removeAllViews();
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
cameraPreviewContainer.addView(cameraPreview, 0, params);
在CameraPreview类中:
@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 (holder.getSurface() == null) {
// preview surface does not exist
return;
}
stopPreview();
// set preview size and make any resize, rotate or
// reformatting changes here
try {
Camera.Size nativePictureSize = CameraUtils.getNativeCameraPictureSize(camera);
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
parameters.setPictureSize(nativePictureSize.width, nativePictureSize.height);
camera.setParameters(parameters);
camera.setDisplayOrientation(90);
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (supportedPreviewSizes != null && optimalSize == null) {
optimalSize = CameraUtils.getOptimalSize(supportedPreviewSizes, width, height);
Log.i(TAG, "optimal size: " + optimalSize.width + "w, " + optimalSize.height + "h");
}
float previewRatio = (float) optimalSize.height / (float) optimalSize.width;
// previewRatio is height/width because camera preview size are in landscape.
float measuredSizeRatio = (float) width / (float) height;
if (previewRatio >= measuredSizeRatio) {
measuredHeight = height;
measuredWidth = (int) ((float)height * previewRatio);
} else {
measuredWidth = width;
measuredHeight = (int) ((float)width / previewRatio);
}
Log.i(TAG, "Preview size: " + width + "w, " + height + "h");
Log.i(TAG, "Preview size calculated: " + measuredWidth + "w, " + measuredHeight + "h");
setMeasuredDimension(measuredWidth, measuredHeight);
}
答案 11 :(得分:-1)
您必须根据所需的宽高比设置cameraView.getLayoutParams()。height和cameraView.getLayoutParams()。width。
答案 12 :(得分:-2)
我放弃了计算并简单地获取了我希望显示相机预览的视图大小,并在我的自定义SurfaceView实现中将相机的预览大小设置为相同(仅因旋转而翻转宽度/高度):
@Override // CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Display display = ((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == Surface.ROTATION_0) {
final Camera.Parameters params = camera.getParameters();
// viewParams is from the view where the preview is displayed
params.setPreviewSize(viewParams.height, viewParams.width);
camera.setDisplayOrientation(90);
requestLayout();
camera.setParameters(params);
}
// I do not enable rotation, so this can otherwise stay as is
}