SurfaceView Camera App 切换相机并修复黑暗预览
SurfaceView Camera App Switch Camera and Fix Dark Preview
我正在使用表面视图构建相机应用程序。该应用程序正确加载相机并捕获图像。图像抓取有两种场景:
图像已捕获,但没有任何内容(即字节数组)发送到预览 activity。在这里工作正常。
抓取图片并将图片回调中的字节数组发送给预览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 没有对这一缺陷进行任何开发。
我正在使用表面视图构建相机应用程序。该应用程序正确加载相机并捕获图像。图像抓取有两种场景:
图像已捕获,但没有任何内容(即字节数组)发送到预览 activity。在这里工作正常。
抓取图片并将图片回调中的字节数组发送给预览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 没有对这一缺陷进行任何开发。