1. 程式人生 > >android使用自定義相機避開部分小米手機app呼叫系統相機有水印會轉向的問題

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秒,你可以根據自己的需求修改為合適的時間