我还没有找到任何一种方法来裁剪摄像头预览并在 SurfaceView 上显示它。
Android - 是否可能裁剪摄像头预览?
我还没有找到任何一种方法来裁剪摄像头预览并在 SurfaceView 上显示它。
Android - 是否可能裁剪摄像头预览?
您可以不使用覆盖视图(这在某些情况下无法工作)来完成此操作。
子类化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 );
}
}
}
actualPreviewWidth
和 actualPreviewHeight
?getWidth()
和 getHeight()
返回 0。我的 CroppedCameraPreview
实例从 XML 文件中膨胀,并使用 match_parent
作为宽度和高度。 - lordmegamaxCamera.getParameters().getSupportedPreviewSizes()
。 - momo<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>
不直接支持。相机API现在不允许偏移,并会将图像压缩到表面持有者中。但是您可以通过放置覆盖层(其他视图)来解决问题。
这里提供了一个针对横屏方向的解决方案,以补充@drees的优秀答案。
只需在res文件夹中添加layout-land文件夹,复制整个布局xml并更改@drees代码中的布局部分,如下所示:
<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>
编程自动滚动图片的代码可能如下所示:
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();
}
<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();
}
});
这是我手机上的截图。中间的灰色斑点是相机透过透明视图看到的地面视图。
这个解决方案是你的情况的一种变通方法。有些代码已经过时,不建议在企业项目中使用,但如果你只需要显示相机预览而不压缩它,那就足够了。
如果你需要在预览之前处理图像,那么你应该看看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;
}
}
}
假设您有一个矩形或浮点矩形,这是您的计算:
float imageWidth = bitmap.Width;
float imageHeight = bitmap.Height;
float width = yourscreenWidth;
float heigth = yourscreenHeight ;
var W= width / heigth / (imageWidth / imageHeight);
var W2 = rect.Width() / widt * W;
var H = rect.Height() / heigth;
var cropImageWidth = imageWidth * W2 ;
var cropImageHeight = imageHeight * H ;
var cropImageX = (imageWidth - cropImageWidth) / 2;
var cropImageY = (imageHeight - cropImageHeight) / 2;
Bitmap imageCropped = Bitmap.CreateBitmap(bitmap, (int)cropImageX, (int)cropImageY, (int)cropImageWidth, (int)cropImageHeight);
经过最近几天的搜索,我相信我需要在这里发布我的解决方案。
我唯一成功做到并且效果良好的是添加了一个比例尺。
我想创建一个带有相机输出部分的TextureView,但如果不让预览缩放,我无法完成它。
所以在我决定相机/屏幕比例的最佳分辨率之后,我获取了相机捕获高度和我想要显示的高度之间的比例尺。
mPreviewSize = chooseOptimalSize(...);
int viewHeight = getDP(this, R.dimen.videoCaptureHeight);
float scaleY = mPreviewSize.getHeight() / viewHeight;
setScaleY(scaleY);