我没有找到任何方法来裁剪相机预览,然后在SurfaceView上显示它。
Android - 是否可以裁剪相机预览?
答案 0 :(得分:16)
您可以在没有叠加视图的情况下执行此操作(这在所有情况下均无效)。
Subclass ViewGroup,将SurfaceView添加为唯一的子项,然后:
基本上,
public class CroppedCameraPreview extends ViewGroup {
private SurfaceView cameraPreview;
public CroppedCameraPreview( Context context ) {
super( context );
// i'd probably create and add the SurfaceView here, but it doesn't matter
}
@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) {
setMeasuredDimension( croppedWidth, croppedHeight );
}
@Override
protected void onLayout( boolean changed, int l, int t, int r, int b ) {
if ( cameraPreview != null ) {
cameraPreview.layout( 0, 0, actualPreviewWidth, actualPreviewHeight );
}
}
}
答案 1 :(得分:9)
您可以将相机预览(SurfaceView)放在ScrollView内的LinearLayout中。当摄像机输出大于您设置的LinearLayout时,您可以通过编程方式滚动它并禁用用户滚动。这样您就可以轻松地模拟相机裁剪:
<ScrollView
android:id="@+id/scrollView"
android:layout_width="640dip"
android:layout_height="282dip"
android:scrollbars="none"
android:fillViewport="true">
<LinearLayout
android:id="@+id/linearLayoutBeautyContent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<SurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/surfaceViewBeautyCamera"/>
</LinearLayout>
</ScrollView>
答案 2 :(得分:6)
不直接。 Camera API现在允许偏移,并将图像压缩到表面 持有人。但是你可以通过在其上放置叠加(其他视图)来解决。
答案 3 :(得分:0)
以编程方式滚动图像的代码将是这样的:
public void setCameraOrientationOnOpen()
{
mCamera.stopPreview();
int rotation = getRotation();
Camera.Parameters currentCameraParameters = mCamera.getParameters();
List<Camera.Size> previewSizes = currentCameraParameters.getSupportedPreviewSizes();
mOptimalCameraSize = getOptimaPreviewCameraSize(previewSizes, (double)9/16);
currentCameraParameters.setPreviewSize(mOptimalCameraSize.width, mOptimalCameraSize.height);
mCamera.setParameters(currentCameraParameters);
float ratio = 100;
int ratio1 = (mSurfaceView.getLayoutParams().height * 100) / mOptimalCameraSize.width; //height
int ratio2 = (mSurfaceView.getLayoutParams().width * 100) / mOptimalCameraSize.height; //width
ratio = Math.max(ratio1, ratio2);
mSurfaceView.getLayoutParams().height = (int) ((mOptimalCameraSize.width * ratio) / 100);
mSurfaceView.getLayoutParams().width = (int) ((mOptimalCameraSize.height * ratio) / 100);
if(ratio > 100)
{
int offset = (mSurfaceView.getLayoutParams().height - mBoxHeight)/2;
mScrollView.scrollTo(0, offset); //center the image
}
mCamera.setDisplayOrientation(rotation);
mOptimalCameraSize = mCamera.getParameters().getPreviewSize();
}
我从相机为我的相机内容框计算最佳预览尺寸(比例16:9),然后将计算出的比率应用于图像以保持相同的比例并最终计算所需的滚动(图像将是在中间)
答案 4 :(得分:0)
创建一个居中的Frame Layout,它将存储Camera Preview并用Views覆盖它以“裁剪”它。创建视图时,动态拉伸也居中的透明视图。
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000" >
<FrameLayout
android:id="@+id/camera_preview_frame"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerVertical="true" />
<View
android:id="@+id/transparent_window"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:layout_centerVertical="true"
android:background="@android:color/transparent" />
<View
android:id="@+id/black_top_box"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/transparent_window"
android:background="#000"/>
<View
android:id="@+id/black_bottom_box"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/transparent_window"
android:background="#000"/>
</RelativeLayout>
然后在您的活动类的OnCreate()方法中,您可以像这样拉伸透明视图。
CameraActivity.java
final View transView = findViewById(R.id.transparent_window);
transView.post(new Runnable() {
@Override
public void run() {
RelativeLayout.LayoutParams params;
params = (RelativeLayout.LayoutParams) transView.getLayoutParams();
params.height = transView.getWidth();
transView.setLayoutParams(params);
transView.postInvalidate();
}
});
这是我手机中的screenshot。中间的灰色斑点是通过透明视图的相机对地板的视图
答案 5 :(得分:0)
Here is a solution for orientation = landscape to complete @drees' excellent answer.
Just add layout-land folder in res folder, duplicate your entire layout xml and change the layout part of @drees' code to be like this:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_dark" >
<FrameLayout
android:id="@+id/frameSurface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:background="@android:color/background_light"/>
<View
android:id="@+id/transparent_window"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:background="@android:color/transparent" />
<View
android:id="@+id/black_top_box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@id/transparent_window"
android:background="@android:color/background_dark"/>
<View
android:id="@+id/black_bottom_box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toRightOf="@id/transparent_window"
android:background="@android:color/background_dark"/>
</RelativeLayout>
答案 6 :(得分:0)
此解决方案是针对您的情况的更多解决方案之一。某些代码已被弃用且不推荐在企业项目中使用,但如果您只需显示相机预览而不会挤压它就足够了。
如果您在预览之前需要处理图像,那么您应该看SurfaceTexture
public class CameraPreview
extends SurfaceView
implements SurfaceHolder.Callback, Camera.PreviewCallback {
public static final String TAG = CameraPreview.class.getSimpleName();
private static final int PICTURE_SIZE_MAX_WIDTH = 1280;
private static final int PREVIEW_SIZE_MAX_WIDTH = 640;
private static final double ASPECT_RATIO = 3.0 / 4.0;
private Camera mCamera;
private SurfaceHolder mHolder;
private boolean mIsLive;
private boolean mIsPreviewing;
public CameraPreview(Context context) {
super(context);
init(context);
}
public CameraPreview(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = (int) (width / ASPECT_RATIO + 0.5);
setMeasuredDimension(width, height);
}
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
//L.g().d(TAG, "onVisibilityChanged: visibility=" + visibility);
if (mIsLive) {
if (visibility == VISIBLE && !mIsPreviewing) {
startCameraPreview();
} else {
stopCameraPreview();
}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
startCamera();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopCamera();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
//L.g().d(TAG, "surfaceChanged: format=" + format + ", width=" + w + ", height=" + h);
if (mHolder.getSurface() == null || mCamera == null) return;
mHolder = holder;
try {
mCamera.stopPreview();
} catch (Exception ignored) {}
try {
mCamera.setPreviewDisplay(mHolder);
if (mIsLive && mIsPreviewing) mCamera.startPreview();
} catch (Exception ignored) {}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//work with camera preview
if (mIsPreviewing) camera.setOneShotPreviewCallback(this);
}
private Camera.Size determineBestPreviewSize(Camera.Parameters parameters) {
return determineBestSize(parameters.getSupportedPreviewSizes(), PREVIEW_SIZE_MAX_WIDTH);
}
private Camera.Size determineBestPictureSize(Camera.Parameters parameters) {
return determineBestSize(parameters.getSupportedPictureSizes(), PICTURE_SIZE_MAX_WIDTH);
}
/**
* This code I found in this repository
* https://github.com/boxme/SquareCamera/blob/master/squarecamera/src/main/java/com/desmond/squarecamera/CameraFragment.java#L368
*/
private Camera.Size determineBestSize(List<Camera.Size> sizes, int widthThreshold) {
Camera.Size bestSize = null;
Camera.Size size;
int numOfSizes = sizes.size();
for (int i = 0; i < numOfSizes; i++) {
size = sizes.get(i);
boolean isDesireRatio = (size.width / 4) == (size.height / 3);
boolean isBetterSize = (bestSize == null) || size.width > bestSize.width;
if (isDesireRatio && isBetterSize) {
bestSize = size;
}
}
if (bestSize == null) {
return sizes.get(sizes.size() - 1);
}
return bestSize;
}
private void init(Context context) {
mHolder = getHolder();
mHolder.addCallback(this);
}
public void startCamera() {
if (!mIsLive) {
//L.g().d(TAG, "startCamera");
mIsPreviewing = false;
mCamera = Camera.open();
if (mCamera != null) {
try {
Camera.Parameters param = mCamera.getParameters();
Camera.Size bestPreviewSize = determineBestPreviewSize(param);
Camera.Size bestPictureSize = determineBestPictureSize(param);
param.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
param.setPictureSize(bestPictureSize.width, bestPictureSize.height);
mCamera.setParameters(param);
} catch (RuntimeException ignored) {}
try {
mCamera.setDisplayOrientation(90);
mCamera.setPreviewCallback(this);
mCamera.setPreviewDisplay(mHolder);
mIsLive = true;
} catch (Exception ignored) {}
}
//else L.g().d(TAG, "startCamera: error launching the camera");
}
}
public void stopCamera() {
if (mCamera != null && mIsLive) {
//L.g().d(TAG, "stopCamera");
mCamera.stopPreview();
mCamera.release();
mCamera = null;
mIsPreviewing = false;
mIsLive = false;
}
}
public void startCameraPreview() {
if (mCamera != null && mIsLive && !mIsPreviewing) {
//L.g().d(TAG, "startCameraPreview");
mCamera.setPreviewCallback(this);
mCamera.startPreview();
mIsPreviewing = true;
}
}
public void stopCameraPreview() {
if (mCamera != null && mIsLive && mIsPreviewing) {
//L.g().d("stopCameraPreview");
mCamera.stopPreview();
mIsPreviewing = false;
}
}
}
答案 7 :(得分:0)
我唯一能做到的并且运作良好的是添加比例。
我想用相机输出的一部分创建一个纹理视图,但是如果不让预览得到破坏,我就无法做到这一点。
所以在我决定相机/屏幕比率开始捕捉的最佳分辨率后,我得到相机捕捉高度和我想要显示的高度之间的比例。
mPreviewSize = chooseOptimalSize(...);
int viewHeight = getDP(this, R.dimen.videoCaptureHeight);
float scaleY = mPreviewSize.getHeight() / viewHeight;
setScaleY(scaleY);
答案 8 :(得分:0)
假设您的Rect或RecF是您的计算值
<%= f.select(:city_id, options_for_select(City.pluck(:name, :id), target_blank: true )) %>