1.需求

我们要求很简单,就是拍照后显示效果要横屏拍的横着显示,竖屏拍着竖屏显示。但是我的手机小米5x等小米型号,存在横竖使用系统相机拍摄都是横屏显示的问题。更惨的是获取旋转角度什么的始终是0,没办法弃疗使用自定义相机,顺便可以去掉系统相机的水印。
简单归纳为以下一点:
横竖屏拍摄后,竖屏状态下查看图片始终是正的

2.选材

自己做个相机在我的项目中没有必要,我要的是普通相机,不用美颜高级功能,所以找个好的开源项目然后修改是我的目标。
目前只翻到两个github开源项目符合我的需求:
a.​​​JCameraView​​​
b.​​​CameraKit​

这两个都可以,相对来说JCameraView更细致一些,有一些动画,有对焦功能,所以选用了这个。

3.改造

1.去除顶部栏,把闪光灯功能放在右边

这是JCameraView内的项目图,实际上除了右上角的前置后置镜头切换,右上角还新增了一个闪光灯功能

android使用自定义相机避开部分小米手机app调用系统相机有水印会转向的问题_ide


但我思考了一下,右上角的镜头翻转在我的项目里不需要,直接invisible隐藏了,把闪光灯移动到了拍摄按钮右边,这样使用上更方便简洁一些。

修改了cameraApplication下的布局camera_view.xml

修改后如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#000000"
android:orientation="vertical">

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">

<VideoView
android:id="@+id/video_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<ImageView
android:id="@+id/image_photo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
android:visibility="invisible"/>
</FrameLayout>

<!--必须有一个有高度的控件在上方,否则焦点框偏上方时会看不到-->
<TextView
android:layout_width="match_parent"
android:layout_height="10px"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:orientation="horizontal">

<ImageView
android:id="@+id/image_flash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="invisible"
android:src="@drawable/ic_flash_on"/>

<ImageView
android:id="@+id/image_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="invisible"
android:src="@drawable/ic_camera"/>

</LinearLayout>


<com.cjt2325.cameralibrary.CaptureLayout
android:id="@+id/capture_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:iconRight="@drawable/ic_flash_on"
android:layout_gravity="bottom"/>

<com.cjt2325.cameralibrary.FoucsView
android:id="@+id/fouce_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="invisible"
</FrameLayout>

JCameraView类里面不显示右上角控件,把两句visible隐藏掉,这个不写了,很容易找;JCameraView类右侧按钮点击需改为:

mCaptureLayout.setRightClickListener(new ClickListener() {
@Override
public void onClick() {
// if (rightClickListener != null) {
// rightClickListener.onClick();
// }
private void setRightFlashRes() {
switch (type_flash) {
case TYPE_FLASH_AUTO:
mCaptureLayout.setRightResource(R.drawable.ic_flash_auto);
machine.flash(Camera.Parameters.FLASH_MODE_AUTO);
break;
case TYPE_FLASH_ON:
mCaptureLayout.setRightResource(R.drawable.ic_flash_on);
machine.flash(Camera.Parameters.FLASH_MODE_ON);
break;
case TYPE_FLASH_OFF:
mCaptureLayout.setRightResource(R.drawable.ic_flash_off);
machine.flash(Camera.Parameters.FLASH_MODE_OFF);
break;

2.去掉录像功能

此相机长按具有录像功能,需对jcamera进行设置:
修改CameraActivity:

jCameraView.setFeatures(JCameraView.BUTTON_STATE_ONLY_CAPTURE);//设置为只能拍照

3.去掉录音权限请求

不用录视频也就用不到录音,需修改MainActivity:

/**
* 获取权限
*/
private void getPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager
.PERMISSION_GRANTED &&
// ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager
// .PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager
.PERMISSION_GRANTED) {
startActivityForResult(new Intent(MainActivity.this, CameraActivity.class), 100);
} else {
//不具有获取权限,需要进行权限申请
ActivityCompat.requestPermissions(MainActivity.this, new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
// Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA}, GET_PERMISSION_REQUEST);
}
} else {
startActivityForResult(new Intent(MainActivity.this, CameraActivity.class), 100);
}
}
@TargetApi(23)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == GET_PERMISSION_REQUEST) {
int size = 0;
if (grantResults.length >= 1) {
int writeResult = grantResults[0];
//读写内存权限
boolean writeGranted = writeResult == PackageManager.PERMISSION_GRANTED;//读写内存权限
if (!writeGranted) {
size++;
}
//相机权限
int cameraPermissionResult = grantResults[1];
boolean cameraPermissionGranted = cameraPermissionResult == PackageManager.PERMISSION_GRANTED;
if (!cameraPermissionGranted) {
size++;
}
if (size == 0) {
startActivityForResult(new Intent(MainActivity.this, CameraActivity.class), 100);
} else {
Toast.makeText(this, "请到设置-权限管理中开启", Toast.LENGTH_SHORT).show();
}
}
}
}

4.自动对焦

这里提一点,因为VideoView这个控件有点奇葩,或者说手机厂商原因,此控件上方下方必须都有遮盖物(要有宽高),不然中间放置的对焦框就可能会在某些位置时不可见。
以下全部为修改​​​JCameraView.java​​​
首先,此对焦框需要对焦可见:

//对焦框指示器动画
private void setFocusViewWidthAnimation(float x, float y) {
if(mPhoto.getVisibility() != View.VISIBLE)
mFoucsView.setVisibility(View.VISIBLE);
machine.foucs(x, y, new CameraInterface.FocusCallback() {
@Override
public void focusSuccess() {
mFoucsView.setVisibility(INVISIBLE);
}
});
}
@Override
public void showPicture(Bitmap bitmap, boolean isVertical) {
if (isVertical) {
mPhoto.setScaleType(ImageView.ScaleType.FIT_XY);
} else {
mPhoto.setScaleType(ImageView.ScaleType.FIT_CENTER);
}
captureBitmap = bitmap;
mPhoto.setImageBitmap(bitmap);
mPhoto.setVisibility(VISIBLE);
mFoucsView.setVisibility(INVISIBLE);
mCaptureLayout.startAlphaAnimation();
mCaptureLayout.startTypeBtnAnimator();
@Override
public boolean handlerFoucs(float x, float y) {
if (y > mCaptureLayout.getTop()) {
return false;
}
if (x < mFoucsView.getWidth() / 2) {
x = mFoucsView.getWidth() / 2;
}
if (x > layout_width - mFoucsView.getWidth() / 2) {
x = layout_width - mFoucsView.getWidth() / 2;
}
if (y < mFoucsView.getWidth() / 2) {
y = mFoucsView.getWidth() / 2;
}
if (y > mCaptureLayout.getTop() - mFoucsView.getWidth() / 2) {
y = mCaptureLayout.getTop() - mFoucsView.getWidth() / 2;
}
if(mPhoto.getVisibility() != View.VISIBLE)
mFoucsView.setVisibility(View.VISIBLE);
mFoucsView.setX(x - mFoucsView.getWidth() / 2);
mFoucsView.setY(y - mFoucsView.getHeight() / 2);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(mFoucsView, "scaleX", 1, 0.6f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(mFoucsView, "scaleY", 1, 0.6f);
ObjectAnimator alpha = ObjectAnimator.ofFloat(mFoucsView, "alpha", 1f, 0.4f, 1f, 0.4f, 1f, 0.4f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(scaleX).with(scaleY).before(alpha);
animSet.setDuration(400);
animSet.start();
return true;

这里判断了mPhotoView,mPhotoView是拍摄完成后用来显示拍摄结果的,当显示拍摄结果的时候,按道理说应该不可见对焦框,所以关掉。

然后我们使用Handler来进行定时对焦,这里我取的是5秒:

public void onResume() {
LogUtil.i("JCameraView onResume");
resetState(TYPE_DEFAULT); //重置状态
CameraInterface.getInstance().registerSensorManager(mContext);
CameraInterface.getInstance().setSwitchView(mSwitchCamera, mFlashLamp);
machine.start(mVideoView.getHolder(), screenProp);
if(handler == null){
handler = new MyHandler(this);
}
handler.sendEmptyMessage(1);
}

private MyHandler handler;

//生命周期onPause
public void onPause() {
LogUtil.i("JCameraView onPause");
stopVideo();
resetState(TYPE_PICTURE);
CameraInterface.getInstance().isPreview(false);
CameraInterface.getInstance().unregisterSensorManager(mContext);
if(handler!=null)
handler.removeCallbacksAndMessages(null);
}

初始化时,取屏幕中心为聚焦x,y,之后取点击点:

2.0f;
focusy = getScreenHeight()/2.0f;
private float getScreenHeight(){
WindowManager manager = ((Activity)getContext()).getWindowManager();
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}

private float getScreenWidth(){
WindowManager manager = ((Activity)getContext()).getWindowManager();
DisplayMetrics outMetrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(outMetrics);
return

onTouchEvent->ACTION_DOWN添加:

event.getX();
focusy = event.getY();

handler:

public static class MyHandler extends Handler{
private WeakReference<JCameraView> wview;
public MyHandler(JCameraView view){
wview = new WeakReference<JCameraView>(view);
}

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(wview == null || wview.get() == null){
removeCallbacksAndMessages(this);
return;
}

JCameraView view = wview.get();
view.setFocusViewWidthAnimation(view.focusx,view.focusy);
postDelayed(new Runnable() {
@Override
public void run() {
sendEmptyMessage(1);
}
},5000);
}
}

这里我定的是5秒,你可以根据自己的需求修改为合适的时间