Android Camera學習(一):如何實現轉動螢幕介面選單跟著轉動效果
阿新 • • 發佈:2019-02-04
最近公司在做車載專案,需要把照相機原本豎向顯示改為橫向顯示。所以研究了下camera選單朝向的問題。
系統提供了一個監聽sensor狀態變化的類OrientationEventListener。在系統程式碼CameraActivity中就是繼承的這個類。
這個類在onCrate方法中構造物件mOrientationListener = new MyOrientationEventListener(this);,在onResume方法中開始監聽 mOrientationListener.enable();,在onPause()方法中取消監聽mOrientationListener.disable();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(); } } }
接下來,我們仔細分析下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介面差不多的。