SurfaceView Camera App 切换相机并修复黑暗预览

SurfaceView Camera App Switch Camera and Fix Dark Preview

我正在使用表面视图构建相机应用程序。该应用程序正确加载相机并捕获图像。图像抓取有两种场景:

  1. 图像已捕获,但没有任何内容(即字节数组)发送到预览 activity。在这里工作正常。

  2. 抓取图片并将图片回调中的字节数组发送给预览activity.

在第二种情况下,应用程序显示黑屏并且没有响应。我不相信我知道这是什么原因,因为我已经检查了许多教程并看到了相同的代码。

到目前为止,这是我的代码:

public class ImageActivity extends AppCompatActivity implements SurfaceHolder.Callback {

private static final String TAG = ImageActivity.class.getSimpleName();

private ImageButton closeIV;
private ImageView flipCameraIV;
private FloatingActionButton fab_capture;
private LinearLayout galleryLayout;

private Camera camera;
private Camera.PictureCallback jpegCallback;
private SurfaceView surfaceView;
private SurfaceHolder holder;

private int currCamId = 0;

private boolean isGalleryImg = false;
private boolean isCameraImg = false;

private byte[] camBytes = null;

private static final int GAL_REQ_CODE = 240;
private static final int CAM_PERM_CODE = 242;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.activity_image);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getWindow().setStatusBarColor(getResources().getColor(R.color.black));
    }

    init();

    holder = surfaceView.getHolder();
    holder.addCallback(this);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    currCamId = Camera.CameraInfo.CAMERA_FACING_BACK;

}

private void handleClicks() {

    closeIV.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            finish();
        }
    });

    flipCameraIV.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Log.d(TAG, "Flip Clicked");
            switchCamera();
        }
    });

    galleryLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            isGalleryImg = true;
            Log.d(TAG, "Gallery Button Clicked");
            Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
            galleryIntent.setType("image/*");
            startActivityForResult(galleryIntent, GAL_REQ_CODE);
        }
    });

    fab_capture.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            captureImage();
        }
    });

    jpegCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] bytes, Camera camera) {
            assert bytes != null;
            Log.d(TAG, "Camera Bytes Array Length:\t" + bytes.length);

            isCameraImg = true;

            ByteArrayOutputStream baos = new ByteArrayOutputStream();


            Intent intent = new Intent(ImageActivity.this, PreviewActivity.class);
            intent.putExtra("camera_bytes", bytes);
            intent.putExtra("fromCamera", isCameraImg);
            startActivity(intent);
            return;

            //refreshCamera();
        }
    };

}


private void captureImage() {
    camera.takePicture(null, null, jpegCallback);
}

public void refreshCamera() {
    if (holder.getSurface() == null) {
        return; // preview surface is empty
    }

    // stop preview, then make changes
    try {
        camera.stopPreview();
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    try {
        camera.setPreviewDisplay(holder);
        camera.startPreview();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

private void init() {
    closeIV = findViewById(R.id.closeIV);
    fab_capture = findViewById(R.id.fab_capture);
    galleryLayout = findViewById(R.id.galleryLayout);
    flipCameraIV = findViewById(R.id.flipCameraIV);
    surfaceView = findViewById(R.id.surfaceView);

    handleClicks();

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == GAL_REQ_CODE && resultCode == RESULT_OK) {
        Uri imgUri = data.getData();
        Intent intent = new Intent(ImageActivity.this, PreviewActivity.class);
        intent.putExtra("fromGallery", isGalleryImg);
        intent.putExtra("gallery_image", imgUri.toString());
        startActivity(intent);
    }

}

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    for (int mResults : grantResults) {
        if (mResults == PackageManager.PERMISSION_DENIED) {
            requestPermissions(new String[]{Manifest.permission.CAMERA,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    CAM_PERM_CODE);
        } else {
            Toast.makeText(ImageActivity.this, "Permission Already Granted", Toast.LENGTH_SHORT).show();
        }
    }

}

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    openCamera();
}

private void openCamera() {
    try {
        camera = Camera.open(currCamId);
    } catch (RuntimeException rex) {
        rex.printStackTrace();
    }

    Camera.Parameters parameters = camera.getParameters();
    parameters.setPreviewFrameRate(20);
    parameters.setPreviewSize(352, 288);
    camera.setDisplayOrientation(90);
    camera.setParameters(parameters);

    try {
        camera.setPreviewDisplay(holder);
    } catch (IOException e) {
        e.printStackTrace();
    }
    camera.startPreview();
}

@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    refreshCamera();
}

@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
    camera.stopPreview();
    camera.release();
    camera = null;

}

@Override
protected void onPause() {
    super.onPause();
    camera.stopPreview();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (camera != null){
        camera.stopPreview();
        camera.release();
        camera = null;
    }
}

}

此外,我尝试使用此代码段切换到前置摄像头并再次切换回来,但没有用。

private void switchCamera() {

    // code I tried to switch camera with but not working

    if (currCamId == Camera.CameraInfo.CAMERA_FACING_BACK){
        currCamId = Camera.CameraInfo.CAMERA_FACING_FRONT;
    } else {
        currCamId = Camera.CameraInfo.CAMERA_FACING_BACK;
    }

    camera = Camera.open(currCamId);
    Camera.CameraInfo info = new Camera.CameraInfo();
    Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, info);

    int rotation = this.getWindowManager().getDefaultDisplay().getRotation();
    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 rotate = (info.orientation - degrees + 360) % 360;
    Camera.Parameters parameters = camera.getParameters();
    parameters.setRotation(rotate);

    try {
        camera.setPreviewDisplay(surfaceView.getHolder());
    } catch (IOException e) {
        e.printStackTrace();
    }

    camera.setParameters(parameters);
    camera.setDisplayOrientation(90);
    camera.startPreview();

    //openCamera();

}

我需要帮助来解决这个问题。谢谢

据我所知,您正在尝试将图片作为字节数组发送,这是不可能的。因为自 api23 起对意图大小有限制。发送 extras 时,必须考虑 extras 的大小,它确实会破坏用户体验并导致新活动加载缓慢。

无论如何,您的字节数组对于所有图片数据来说太大了,我认为这会导致错误。你必须做什么?您必须先将该字节数组保存为 jpeg 文件,然后将文件路径添加到您的意图中。 Take a look to this solution

哦,还有,只是一个基于经验的友好建议,最好使用一个好的相机库而不是android-sdk的经典用法。正如我所说,android 开发中最糟糕的部分是关于媒体的任何事情,特别是相机 api。与 ios 相比,这是令人羞愧的,而且自 2015 年以来,google 没有对这一缺陷进行任何开发。