android使用自定義相機避開部分小米手機app呼叫系統相機有水印會轉向的問題
1.需求
我們要求很簡單,就是拍照後顯示效果要橫屏拍的橫著顯示,豎屏拍著豎屏顯示。但是我的手機小米5x等小米型號,存在橫豎使用系統相機拍攝都是橫屏顯示的問題。更慘的是獲取旋轉角度什麼的始終是0,沒辦法棄療使用自定義相機,順便可以去掉系統相機的水印。
簡單歸納為以下一點:
橫豎屏拍攝後,豎屏狀態下檢視圖片始終是正的
2.選材
自己做個相機在我的專案中沒有必要,我要的是普通相機,不用美顏高階功能,所以找個好的開源專案然後修改是我的目標。
目前只翻到兩個github開源專案符合我的需求:
a.JCameraView
b.CameraKit
這兩個都可以,相對來說JCameraView更細緻一些,有一些動畫,有對焦功能,所以選用了這個。
3.改造
1.去除頂部欄,把閃光燈功能放在右邊
這是JCameraView內的專案圖,實際上除了右上角的前置後置鏡頭切換,右上角還新增了一個閃光燈功能
但我思考了一下,右上角的鏡頭翻轉在我的專案裡不需要,直接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();
// }
mFlashLamp.performClick();
setRightFlashRes();
}
});
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,之後取點選點:
focusx =getScreenWidth()/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 outMetrics.widthPixels;
}
onTouchEvent->ACTION_DOWN新增:
focusx = 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秒,你可以根據自己的需求修改為合適的時間