1. 程式人生 > >Android Camera學習(一):如何實現轉動螢幕介面選單跟著轉動效果

Android Camera學習(一):如何實現轉動螢幕介面選單跟著轉動效果

最近公司在做車載專案,需要把照相機原本豎向顯示改為橫向顯示。所以研究了下camera選單朝向的問題。

系統提供了一個監聽sensor狀態變化的類OrientationEventListener。在系統程式碼CameraActivity中就是繼承的這個類。

 private class MyOrientationEventListener
            extends OrientationEventListener {
        public MyOrientationEventListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            // We keep the last known orientation. So if the user first orient
            // the camera then point the camera to floor or sky, we still have
            // the correct orientation.
            if (orientation == ORIENTATION_UNKNOWN) {
                return;
            }
            if (mOrientationCorrectedValue != -1)
                orientation = mOrientationCorrectedValue + orientation;
            mLastRawOrientation = orientation;
            mCurrentModule.onOrientationChanged(orientation);
            OnScreenHint.globalOrientaion = orientation;
            if (mStorageHint != null)
            {
                mStorageHint.onOrientationChanged();
            }
        }
    }
這個類在onCrate方法中構造物件mOrientationListener = new MyOrientationEventListener(this);,在onResume方法中開始監聽 mOrientationListener.enable();,在onPause()方法中取消監聽mOrientationListener.disable();

接下來,我們仔細分析下onOrientationChanged(int orientation)程式碼:當我們轉動手機時,sensor值就會發生變化就會呼叫這個方法。

mOrientationCorrectedValue這個值在oncreta方法中繼續了賦值 mOrientationCorrectedValue = CameraUtil.getInitOrientation(); 

   public static int getInitOrientation() {
        return mInitOrientation;
    }

    public static void updateInitOrientation() {
        IBinder displayToken = SurfaceControl.getBuiltInDisplay(0);
        if (displayToken != null && SurfaceControl.getDisplayInfo(displayToken, mTempPhys)) {
            Log.d(TAG, "displayinfo width:" + mTempPhys.width + " height:" + mTempPhys.height);
            mInitOrientation =  mTempPhys.width > mTempPhys.height ? 270 : 0;
        }
    }
這裡就是判斷你手機是橫屏手機還是豎屏手機的地方。

mCurrentModule.onOrientationChanged(orientation); 這句程式碼才是這個監聽的核心。

 private CameraModule mCurrentModule; CameraModule類是一個介面類,這個地方是由 PhotoModule  implements CameraModule實現的。

    public void onOrientationChanged(int orientation) {
        // We keep the last known orientation. So if the user first orient
        // the camera then point the camera to floor or sky, we still have
        // the correct orientation.
        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
        mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
        // Show the toast after getting the first orientation changed.
        if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
            mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
            showTapToFocusToast();
        }
        mUI.onOrientationChanged(mOrientation);
        if (mParameters != null && (mOldOrientation != mOrientation) && mCameraId != -1
                && mCameraDevice != null && mOrientation % 90 == 0) {
            mParameters.setRotation(mMirror ? (720 - (mOrientation + mDisplayOrientation)) % 360
                    : (mOrientation + mDisplayOrientation) % 360);
            mCameraDevice.setParameters(mParameters);
            Log.i(TAG, "onOrientationChanged()  mOrientation=" + mOrientation
                    + "mDisplayOrientation=" + CameraUtil.getCameraOrientation(mCameraId) + mMirror);
        }
        if (mAllowRotateMemoryUseToast && mRotateMemoryUseToast != null) {
            mRotateMemoryUseToast.onOrientationChanged(mOrientation);
        }
        mOldOrientation = mOrientation;
    }

mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);這句就是由sensor給的值取方向的核心程式碼。

  public static int roundOrientation(int orientation, int orientationHistory) {
        boolean changeOrientation = false;
        if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) {
            changeOrientation = true;
        } else {
            int dist = Math.abs(orientation - orientationHistory);
            dist = Math.min( dist, 360 - dist );
            changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS );
        }
        if (changeOrientation) {
            return ((orientation + 45) / 90 * 90) % 360;
        }
        return orientationHistory;
    }
 mUI.onOrientationChanged(mOrientation);而這句就是更具朝向來顯示UI效果。  private PhotoUI mUI;
 public void onOrientationChanged(int orientation) {
        // Log.d(TAG, "orientation = "+orientation);
        if (orientation == OrientationListener.ORIENTATION_UNKNOWN)
            return;
        orientation = orientation % 360;
        mOrientation = orientation;
        if (mModuleView != null) {
            mModuleView.setOrientation(orientation, true);
        }
        if (mCameraEffectView != null) {
            mCameraEffectView.setOrientation(orientation, false);
        }

        if (mPreviewThumb != null) {
            mPreviewThumb.setOrientation(orientation, true);
        }

        if ((mSwitcher != null) && (mSwitcherBg != null)) {
            mSwitcher.setOrientation(orientation, true);
            mSwitcherBg.setOrientation(orientation, true);
        }

        if (mShutterButton != null) {
            mShutterButton.setOrientation(orientation, true);
        }

        if (mSwitchCameraVideoLayout != null) {
            mSwitchCameraVideoLayout.setOrientation(orientation, true);
        }

        if (Settings.System.getInt(mActivity.getContentResolver(),
                Settings.System.ACCELEROMETER_ROTATION, 0) == 1) {
            if (mCameraIntentShowLayout != null) {
                mCameraIntentShowLayout.setOrientation(orientation, false);
            }
        }

        if (mBurstToastLayout != null) {
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mBurstModeLayout
                    .getLayoutParams();
            lp.setMargins(0, 0, 0, CameraUtil.dpToPixel((orientation % 180==0)? 60:30));
            mBurstModeLayout.setLayoutParams(lp);
            mBurstToastLayout.setOrientation(orientation, false);
        }
        showModuleText();

        if (mPopup != null) {
            mPopup.setOrientation(orientation, false);
        }
        if (mPopupBottomMenu != null) {
            if (mPopupBottomMenu.isShowing()) {
                mPopupBottomMenu.setOrientation(orientation, false);
                mPopupBottomMenu.showAccordOrientation();
            }
            mPopupBottomMenu.setOrientation(orientation, false);
        }

        if (mDMenuUi != null) {
            mDMenuUi.onOrientationChanged(orientation);
        }
        if (mZoomRenderer != null) {
            mZoomRenderer.setOrientation(orientation, false);
        }
    }
這段程式碼可以看出camera將整體介面封裝成一個個單獨的view,有view來顯示不同的ui效果,這裡mModuleView為例分析    private ModuleSwitchView mModuleView;
public void setOrientation(int orientation, boolean animation) {
        // TODO Auto-generated method stub
        if (mOrientation == orientation) return;
        mOrientation = orientation;
        if (mModuleToastLayout != null && mModuleToastLinearLayout != null) {
            mModuleToastLayout.setOrientation(orientation, false);
            switch (orientation) {
            case 0:
                mModuleToastLinearLayout
                        .setBackgroundResource(R.drawable.description_bubble_popup_01);
                break;
            case 90:
                mModuleToastLinearLayout
                        .setBackgroundResource(R.drawable.description_bubble_popup_04);
                break;
            case 180:
                mModuleToastLinearLayout
                        .setBackgroundResource(R.drawable.description_bubble_popup_02);
                break;
            case 270:
                mModuleToastLinearLayout
                        .setBackgroundResource(R.drawable.description_bubble_popup_03);
                break;
            }
        }
        if (mButtonGrid != null) {
            mButtonGrid.setOrientation(orientation, true);
        }
        if (mButtonAuto != null) {
            mButtonAuto.setOrientation(orientation, true);
        }
        if (mRotateGridModule != null) {
            LayoutParams lp = (FrameLayout.LayoutParams) mGridViewModule.getLayoutParams();
            if (orientation % 180 == 0) {
                lp.setMargins(0, CameraUtil.dpToPixel(60), 0, CameraUtil.dpToPixel(60));
            } else {
                lp.setMargins(CameraUtil.dpToPixel(60), 0, CameraUtil.dpToPixel(60), 0);
            }
            mGridViewModule.setLayoutParams(lp);
            mGridViewModule.setOrientation(orientation, false);
            mRotateGridModule.setOrientation(orientation, false);
        }
        if (mGalleryModule != null) {
            mGalleryModule.setOrientation(orientation, false);
        }
    }
可以看出根據不同的orientation值,來獲取對應的ui介面。有興趣的小夥伴可以分析分析別的view程式碼,可以學到很多新知識。

同樣的video介面的朝向,也是和camera介面差不多的。