最近公司在做车载项目,需要把照相机原本竖向显示改为横向显示。所以研究了下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界面差不多的。