这一篇文章其实和上一篇文章类似,但是好处在于已经解决了上一篇文章结尾处提到的OPPO手机的问题。本文就不过多的文字描述了,只有两个部分,第一部分说一下SurfaceView
监听回调方法的意思,第二部分就直接粘贴代码了。
监听
预览页面需要实现的监听的接口如下:
public interface Callback {
/**
* This is called immediately after the surface is first created.
* Implementations of this should start up whatever rendering code
* they desire. Note that only one thread can ever draw into
* a {@link Surface}, so you should not draw into the Surface here
* if your normal rendering will be in another thread.
*
* @param holder The SurfaceHolder whose surface is being created.
*/
public void surfaceCreated(SurfaceHolder holder);
/**
* This is called immediately after any structural changes (format or
* size) have been made to the surface. You should at this point update
* the imagery in the surface. This method is always called at least
* once, after {@link #surfaceCreated}.
*
* @param holder The SurfaceHolder whose surface has changed.
* @param format The new PixelFormat of the surface.
* @param width The new width of the surface.
* @param height The new height of the surface.
*/
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height);
/**
* This is called immediately before a surface is being destroyed. After
* returning from this call, you should no longer try to access this
* surface. If you have a rendering thread that directly accesses
* the surface, you must ensure that thread is no longer touching the
* Surface before returning from this function.
*
* @param holder The SurfaceHolder whose surface is being destroyed.
*/
public void surfaceDestroyed(SurfaceHolder holder);
}
-
surfaceCreated
:每次初始化预览界面时,都会执行一次这个方法,用于初始化相机资源。 -
surfaceChanged
:当预览界面布局发生改变时执行一次,与TextureView
的onSurfaceTextureSizeChanged
一样。但是区别是:SurfaceView
在初始化时就会执行surfaceChanged
方法,但是TextureView
在初始化时不会执行onSurfaceTextureSizeChanged
。 -
surfaceDestroyed
:当预览界面切入后台或者Activity销毁时执行。
实现代码
预览界面类
public class CameraPreview2 extends SurfaceView implements SurfaceHolder.Callback {
private Camera mCamera;
public CameraPreview2(Context context) {
super(context);
}
/**
* 获取相机对象
* @return
*/
public Camera getCamera(){
return mCamera;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera = Camera.open();
mCamera.enableShutterSound(false);
//设置预览方向,预览方向默认是横屏的,所以这里要调整一下预览方向
mCamera.setDisplayOrientation(90);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
//获取最适合的分辨率
Camera.Size previewSize = getCameraSize(parameters.getSupportedPreviewSizes(), holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
parameters.setPreviewSize(previewSize.width, previewSize.height);
//获取最适合的分辨率
Camera.Size pictureSize = getCameraSize(parameters.getSupportedPictureSizes(), holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
parameters.setPictureSize(pictureSize.width, pictureSize.height);
//设置拍照之后,本地图片格式
parameters.setPictureFormat(ImageFormat.JPEG);
//拍照后的图片文件旋转90度
parameters.setRotation(90);
mCamera.setParameters(parameters);
//将相机的预览效果显示出来
mCamera.setPreviewDisplay(holder);
//开启预览
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取最合适的Size
* @param sizeList
* @param width
* @param height
* @return
*/
private Camera.Size getCameraSize(List<Camera.Size> sizeList, int width, int height){
Camera.Size tempSize = null;
float aspectRatio = height * 1.0f / width;//求出预期横宽比
float offset = aspectRatio;//预期横宽比和实际横宽比误差
for(Camera.Size size : sizeList){
if(size.width < height || size.height < width){
continue;
}
//误差最小值
if(Math.abs(aspectRatio - size.width * 1.0f / size.height) < offset){
offset = Math.abs(aspectRatio - size.width * 1.0f / size.height);
tempSize = size;
}
}
if(tempSize == null){
tempSize = sizeList.get(0);
}
return tempSize;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if(mCamera != null){
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
}
在Activity中的运用
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private CameraPreview2 mPreview;
private FrameLayout fl_camera_preview;
private ImageView takePhone;
private ImageView flash_button;
//是否开启闪光灯
private boolean isFlashing;
//图片临时缓存
private byte[] imageData;
private ImageView save_button;
private ImageView cancle_save_button;
private Button cancle_button;
private RelativeLayout ll_photo_layout;
private RelativeLayout ll_confirm_layout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
fl_camera_preview = findViewById(R.id.camera_preview);
takePhone = findViewById(R.id.take_photo_button);
takePhone.setOnClickListener(this);
flash_button = findViewById(R.id.flash_button);
flash_button.setOnClickListener(this);
ll_photo_layout = findViewById(R.id.ll_photo_layout);
ll_confirm_layout = findViewById(R.id.ll_confirm_layout);
save_button = findViewById(R.id.save_button);
save_button.setOnClickListener(this);
cancle_save_button = findViewById(R.id.cancle_save_button);
cancle_save_button.setOnClickListener(this);
cancle_button = findViewById(R.id.cancle_button);
cancle_button.setOnClickListener(this);
// 创建预览界面
mPreview = new CameraPreview2(this);
//添加监听
mPreview.getHolder().addCallback(mPreview);
//将预览页面添加到FrameLayout中
fl_camera_preview.addView(mPreview);
}
/**
* 调用摄像头开始拍照片
*/
private void takePhoto() {
//调用相机拍照
final Camera mCamera = mPreview.getCamera();
if(mCamera == null){
return;
}
//拍照
mCamera.takePicture(null, null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
//视图动画
ll_photo_layout.setVisibility(View.GONE);
ObjectAnimator anim1 = ObjectAnimator.ofFloat(ll_confirm_layout, "scaleX", 0, 1.0f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_confirm_layout, "scaleY", 0, 1.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000);
animatorSet.play(anim1).with(anim2);
animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
animatorSet.start();
ll_confirm_layout.setVisibility(View.VISIBLE);
//停止预览
mCamera.stopPreview();
imageData = data;
}
});
}
/**
* 保存照片
* @param imageData
*/
private void savePhoto(byte[] imageData) {
if(imageData.length == 0){
return;
}
FileOutputStream fos = null;
String cameraPath = Environment.getExternalStorageDirectory().getPath() + File.separator + "1";
//相册文件夹
File cameraFolder = new File(cameraPath);
if (!cameraFolder.exists()) {
cameraFolder.mkdirs();
}
//保存的图片文件
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
String imagePath = cameraFolder.getAbsolutePath() + File.separator + "IMG_" + simpleDateFormat.format(new Date()) + ".jpg";
File imageFile = new File(imagePath);
try {
fos = new FileOutputStream(imageFile);
fos.write(imageData);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 切换闪光灯
*/
private void switchFlash() {
Camera mCamera = mPreview.getCamera();
if(mCamera == null){
return;
}
isFlashing = !isFlashing;
flash_button.setImageResource(isFlashing ? R.mipmap.flash_open : R.mipmap.flash_close);
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode(isFlashing ? Camera.Parameters.FLASH_MODE_TORCH : Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
} catch (Exception e) {
Toast.makeText(this, "该设备不支持闪光灯", Toast.LENGTH_SHORT);
}
}
/**
* 快速切换到预览状态
*/
private void changePreview() {
Camera mCamera = mPreview.getCamera();
if(mCamera == null){
return;
}
ll_confirm_layout.setVisibility(View.GONE);
ObjectAnimator anim1 = ObjectAnimator.ofFloat(ll_photo_layout, "scaleX", 0, 1.0f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_photo_layout, "scaleY", 0, 1.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(1000);
animatorSet.play(anim1).with(anim2);
animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
animatorSet.start();
ll_photo_layout.setVisibility(View.VISIBLE);
//开始预览
mCamera.startPreview();
imageData = null;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.take_photo_button:
//拍照
takePhoto();
break;
case R.id.flash_button:
//切换闪光灯
switchFlash();
break;
case R.id.save_button:
//保存照片
savePhoto(imageData);
//切换到预览状态
changePreview();
break;
case R.id.cancle_save_button:
//切换到预览状态
changePreview();
break;
case R.id.cancle_button:
finish();
break;
}
}
}
一些图片和布局文件和上一篇一样,在上一篇拿即可。
[本章完...]
网友评论