如何在平方 SurfaceView(如 Instagram)中将相机预览大小设置为平方纵横比
Posted
技术标签:
【中文标题】如何在平方 SurfaceView(如 Instagram)中将相机预览大小设置为平方纵横比【英文标题】:How can I set camera preview size to squared aspect ratio in a squared SurfaceView (like Instagram) 【发布时间】:2012-06-22 17:33:39 【问题描述】:我正在尝试开发自己的相机活动,但我有一个我无法解决的问题...
我想要的是和instagram相框非常相似的东西,这就是我得到的:
什么时候我应该得到这样的东西:
还有……
什么时候我应该得到类似的东西:
我认为我可以很好地管理 SurfaceView 和相机预览,仅使用
Camera.Parameters parameters = camera.getParameters();
camera.setDisplayOrientation(90);
和自定义 SurfaceView:
public class SquaredSurfaceView extends SurfaceView
private int width;
private int height;
public SquaredSurfaceView(Context context)
super(context);
public SquaredSurfaceView(Context context, AttributeSet attrs)
super(context, attrs);
public SquaredSurfaceView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = width;
setMeasuredDimension(width, width);
public int getViewWidth()
return width;
public int getViewHeight()
return height;
我做错了什么?? :-(
【问题讨论】:
首先,它们看起来很相似,只是您的 CameraPreview 没有居中。此外,您应该检查以确保您正在寻找的预览大小实际上是由相机支持:Camera.Parameters params = mCamera.getParameters();尺寸 = params.getSupportedPreviewSizes();然后只需遍历尺寸以找到您正在寻找的支持的尺寸(即 1:1,如果它甚至可用)。但是,要使其实际显示为 1:1 的比例,您可能需要剪切部分视图 我已经试过了:mSupportedPreviewSizes = parameters.getSupportedPreviewSizes(); Camera.Size size = mSupportedPreviewSizes.get(2); if (size != null /* && pictureSize != null */) parameters.setPreviewSize(size.width, size.height); .... 但是你没有检查纵横比;您只是将其设置为可用的预览尺寸之一。要获得您要求的正确配给量,您必须比较宽度/高度 我已经做了,但是没用... @m3n0R : 你有什么解决办法我也卡在那里吗? 【参考方案1】:如前所述,您需要找到正确的预览尺寸(宽高比为 1:1 的预览尺寸),并且您可能必须为 SurfacePreview 使用 FrameLayout。看起来你有纵横比问题,也许你有正确的预览大小,但你把它放在不正确的布局中。
另一种解决方案可能是(就像 Instagram 一样)将您的相机设置为全尺寸,然后隐藏布局的某些区域以使其看起来像一个正方形。然后,您必须通过软件将图像剪切成真正的正方形。
希望对你有帮助
【讨论】:
@xramos 我怎么能做 instagram 做的那件事?你有任何参考或指针吗?请帮助我:) 谢谢。 我也对 IG 如何做到这一点很感兴趣。 我想如果已经解释过了。我不知道 instagram 是否真的如此,但似乎如此。让您的相机预览尺寸适用于您的设备,然后在相机框架布局顶部放置一些黑色布局,以使您的可见相机成正方形。稍后拍照时需要进行一些计算。但同样,我不确定他们是否真的这样做 @xramos :您好,正如您提到的,我已经实现了全摄像头,但无法正确裁剪所有设备。所有设备也有不同的相机分辨率。你能帮助我吗?等待您的反馈。【参考方案2】:你可以使用它来获取图片大小:
public static void initialCameraPictureSize(Context context, android.hardware.Camera.Parameters parameters)
List list = parameters.getSupportedPictureSizes();
if(list != null)
android.hardware.Camera.Size size = null;
Iterator iterator = list.iterator();
do
if(!iterator.hasNext())
break;
android.hardware.Camera.Size size1 = (android.hardware.Camera.Size)iterator.next();
if(Math.abs(3F * ((float)size1.width / 4F) - (float)size1.height) < 0.1F * (float)size1.width && (size == null || size1.height > size.height && size1.width < 3000))
size = size1;
while(true);
if(size != null)
parameters.setPictureSize(size.width, size.height);
else
Log.e("CameraSettings", "No supported picture size found");
【讨论】:
这是一个糟糕的答案,因为它假设一个 4:3 的相机。相机可能是 16:9 @Aggressor 这是一个演示。您可以在哪里设置占位符/表面视图的比例。 这个例子当然可以改进以轻松计算比率而不是硬编码【参考方案3】:对我有用的解决方案是第二个答案,但是因为我们需要将相机旋转 90º 才能将 WIDTH 与 HEIGTH 切换,所以像这样......
camera.setDisplayOrientation(90);
Camera.Parameters params= camera.getParameters();
surfaceView.getLayoutParams().width=params.getPreviewSize().height;
surfaceView.getLayoutParams().height=params.getPreviewSize().width;
希望这个解决方案有帮助!! :D
【讨论】:
这对我造成了异常。 感谢您的回答。您能告诉我为什么会这样吗? 它解决了我在 nexus 5x 上的问题,但我仍然遇到 moto g2 的问题,即不同版本的设备的行为不同,或者它们是其他情况。 这个问题我看了半天;这里提出的解决方案:***.com/questions/19577299/…,有点工作,但它可以降低性能(因此不是很好的解决方案)。我已经尝试过您的解决方案,与其他文档中的解决方案相比,它看起来非常简单,我放了一些Log.d(..)
来检查您的代码前后的高度和宽度大小,没有任何改变。【参考方案4】:
我的解决方案更像是构建一个方形蒙版,然后将其放置在预览表面上。
您主要需要三样东西,首先是一个方形框架组件。我制作了一个自定义组件:
package com.example.squaredviewer;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Display;
import android.view.WindowManager;
import android.widget.RelativeLayout;
/**
* Created by yadirhb on 14-08-2015.
*/
public class SquaredFrame extends RelativeLayout
public SquaredFrame(Context context, AttributeSet attrs)
super(context, attrs);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
setMeasuredDimension(size, size);
根据您正在开发的 Android API 版本,您可能需要添加另一个构造函数重载。对于 Kitkat 来说,这很好。
第二步,构建布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
android:orientation="horizontal"
android:visibility="visible">
<RelativeLayout
android:id="@+id/camera_preview"
android:layout_
android:layout_
android:layout_alignParentLeft="false"
android:layout_alignParentTop="false"
android:layout_centerInParent="true"
android:background="#ffffff">
</RelativeLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_
android:layout_>
<LinearLayout
android:orientation="vertical"
android:layout_
android:layout_
android:layout_weight="1"
android:background="#131008">
</LinearLayout>
<com.example.squaredviewer.SquaredFrame
android:layout_
android:layout_
android:layout_centerInParent="true"></com.example.squaredviewer.SquaredFrame>
<LinearLayout
android:orientation="vertical"
android:layout_
android:layout_
android:layout_weight="1"
android:background="#131008" />
</LinearLayout>
</RelativeLayout>
请注意,RelativeLayout "camera_preview" 是用于渲染预览的,它居中并且有一个包含平方组件的 LinearLayout。这实际上是“遮罩”,它覆盖了相机预览。 另请注意,除了透明的 SquaredFrame 之外,其他两个都是黑色背景。
现在是表面视图,用于根据纵横比调整表面大小的相机预览。
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
private final String TAG = "PIC-FRAME";
private SurfaceHolder mHolder;
private Camera mCamera;
private Display display;
public CameraPreview(Activity context, Camera camera)
super(context);
mCamera = camera;
display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
// 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);
setKeepScreenOn(true);
public void surfaceDestroyed(SurfaceHolder holder)
// empty. Take care of releasing the Camera preview in your activity.
this.getHolder().removeCallback(this);
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;
try
// stop preview before making changes
mCamera.stopPreview();
// set preview size and make any resize, rotate or
// reformatting changes here
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = parametes.getSupportedPreviewSizes().get(0);
parameters.setPreviewSize(previewSize.width, previewSize.height);
// start preview with new settings
mCamera.setParameters(parameters);
// Set the holder size based on the aspect ratio
int size = Math.min(display.getWidth(), display.getHeight());
double ratio = (double) previewSize.width / previewSize.height;
mHolder.setFixedSize((int)(size * ratio), size);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
catch (Exception e)
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
现在一切都必须绑定在活动类中
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_taker);
mDecorView = getWindow().getDecorView();
//mCamera = a camera instance;
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
//Layout where camera preview is shown.
RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
//FrameLayout stack controllers inside and superpose them.
preview.addView(mPreview, 0);
// TODO
有点长,但我希望它对不止一个人有帮助。 :-)
【讨论】:
一个令人难以置信的解决方案,YadirHB @YadirHB 布局文件的内容是什么:activity_picture_taker.xml【参考方案5】:太难了。不容易。
第一件事:您定义 2 个黑色 Linear Layout
以获取您发布的 UI。
第二件事:你需要把图片从全图剪成方形图。
如何切割,需要使用scaleBitmap
方法。
或者您想要真正的自定义相机?可以签到here
【讨论】:
答案通常包含更多细节,例如代码或更详细的解释。添加它肯定会使它更有用。【参考方案6】:我在方形视图中遇到了同样的问题 - 只需将 SurfaceView 的相机大小设置为我想要绘制它的视图大小即可解决它。没有复杂的计算,它对我有用。有关整个方法,请在此处查看我的回复:https://***.com/a/39430615/5181489
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
...
params.setPreviewSize(viewParams.height, viewParams.width);
camera.setParameters(params);
【讨论】:
以上是关于如何在平方 SurfaceView(如 Instagram)中将相机预览大小设置为平方纵横比的主要内容,如果未能解决你的问题,请参考以下文章
如何在Android中修复相机预览(surfaceview)的正确纵横比?
如何在surfaceview中将Android相机更改为人像?