本篇主要學(xué)習(xí)Camera的UI布局結(jié)構(gòu)
從布局文件camera.xml開始
<?xml version="1.0" encoding="utf-8"?>
<!-- This layout is shared by phone and tablet in landscape orientation. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/camera_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<FrameLayout android:id="@+id/camera_surfaceview_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
</FrameLayout>
<FrameLayout android:id="@+id/camera_app_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<View android:id="@+id/camera_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:visibility="visible"/>
<include layout="@layout/preview_frame"/>
<include layout="@layout/view_layers"/>
</FrameLayout>
</FrameLayout>
一個完全由FrameLayout構(gòu)建的布局,然后進(jìn)入view_layers.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_layer_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
<!-- review views here -->
<FrameLayout android:id="@+id/view_layer_bottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
</FrameLayout>
<!-- normal views here -->
<RelativeLayout android:id="@+id/view_layer_normal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
>
</RelativeLayout>
<FrameLayout android:id="@+id/view_layer_top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
</FrameLayout>
<FrameLayout android:id="@+id/view_layer_shutter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
>
</FrameLayout>
<FrameLayout android:id="@+id/view_layer_setting"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false"
>
</FrameLayout>
<!-- overlay views here -->
<FrameLayout android:id="@+id/view_layer_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:splitMotionEvents="false">
</FrameLayout>
</FrameLayout>
我們搜索相關(guān)View的id確定它們在CameraAppUiImpl.java中被引用蓉坎,將相關(guān)代碼取出如下:
public class CameraAppUiImpl implements ICameraAppUi {
private ViewGroup mViewLayerBottom;
private ViewGroup mViewLayerNormal;
private ViewGroup mViewLayerTop;
private ViewGroup mViewLayerShutter;
private ViewGroup mViewLayerSetting;
private ViewGroup mViewLayerOverlay;
public CameraAppUiImpl(CameraActivity context) {
Log.i(TAG, "[CameraAppUiImpl] constructor... ");
mCameraActivity = context;
mMainHandler = new MainHandler(context.getMainLooper());
}
public void initializeViewGroup() {
mViewLayerBottom = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_bottom);
mViewLayerNormal = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_normal);
mViewLayerTop = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_top);
mViewLayerShutter = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_shutter);
mViewLayerSetting = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_setting);
mViewLayerOverlay = (ViewGroup) mCameraActivity.findViewById(R.id.view_layer_overlay);
}
...
}
同時查看CameraActivity.java的onCreate方法澳眷,選出相關(guān)聯(lián)的代碼如下
@Override
public void onCreate(Bundle icicle) {
...
mCameraAppUi = new CameraAppUiImpl(this);
mCameraAppUi.createCommonView();
initializeCommonManagers();
mCameraAppUi.initializeCommonView();
mCameraDeviceCtrl.setCameraAppUi(mCameraAppUi);
IFileSaver fileSaver = new FileSaverImpl(mFileSaver);
IFeatureConfig featureConfig = new FeatureConfigImpl();
ICameraDeviceManager deviceManager = new CameraDeviceManagerImpl(this,
mCameraDeviceCtrl);
mISelfTimeManager = new SelfTimerManager(this, mCameraAppUi);
mModuleManager = new ModuleManager(this, fileSaver, mCameraAppUi,
featureConfig, deviceManager, moduleCtrl, mISelfTimeManager);
mISettingCtrl = mModuleManager.getSettingController();
mCameraAppUi.setSettingCtrl(mISettingCtrl);
if (isVideoCaptureIntent() || isVideoWallPaperIntent()) {
mCameraActor = new VideoActor(this, mModuleManager,
ModePicker.MODE_VIDEO);
} else {
mCameraActor = new PhotoActor(this, mModuleManager,
ModePicker.MODE_PHOTO);
}
mCameraDeviceCtrl.setModuleManager(mModuleManager);
mCameraDeviceCtrl.setSettingCtrl(mISettingCtrl);
mCameraDeviceCtrl.setCameraActor(mCameraActor);
mCameraDeviceCtrl.resumeStartUpThread();
mFileSaver.bindSaverService();
mOtherDeviceConectedManager = new ExternalDeviceManager(this);
mOtherDeviceConectedManager.onCreate();
mOtherDeviceConectedManager.addListener(mListener);
// only initialize some thing for open
initializeForOpeningProcess();
initializeAfterPreview();
...
}
我們看到在CameraAppUiImpl初始化后首先執(zhí)行了如下方法:
public void createCommonView() {
mShutterManager = new ShutterManager(mCameraActivity);
mInfoManager = new InfoManager(mCameraActivity);
mRotateProgress = new RotateProgress(mCameraActivity);
mRemainingManager = new RemainingManager(mCameraActivity);
mPickerManager = new PickerManager(mCameraActivity);
mIndicatorManager = new IndicatorManager(mCameraActivity);
mReviewManager = new ReviewManager(mCameraActivity);
mRotateDialog = new RotateDialog(mCameraActivity);
mZoomManager = new ZoomManager(mCameraActivity);
mThumbnailManager = new ThumbnailViewManager(mCameraActivity);
if (FeatureSwitcher.isVfbEnable()) {
mFaceBeautyEntryView = new FaceBeautyEntryView(mCameraActivity); //add for FB entry
}
mSettingManager = new SettingManager(mCameraActivity);
mEffectManager = new EffectViewManager(mCameraActivity, mEffectListener);
// For tablet
if (FeatureSwitcher.isSubSettingEnabled()) {
mSubSettingManager = new SubSettingManager(mCameraActivity);
}
}
從第一個ShutterManager開始看,它繼承自ViewManager蛉艾,從它的getView方法可以查看到所使用的布局文件钳踊,我們確定它就是相機(jī)下方兩個按鈕衷敌,用來切換拍照還是錄相
public class ShutterManager extends ViewManager {
...
private ShutterButton mPhotoShutter;
private ShutterButton mVideoShutter;
private View mOkButton;
private View mCancelButton;
private OnShutterButtonListener mPhotoListener;
private OnShutterButtonListener mVideoListener;
private OnClickListener mOklistener;
private OnClickListener mCancelListener;
public void setShutterListener(OnShutterButtonListener photoListener,
OnShutterButtonListener videoListener, OnClickListener okListener,
OnClickListener cancelListener) {
mPhotoListener = photoListener;
mVideoListener = videoListener;
mOklistener = okListener;
mCancelListener = cancelListener;
applyListener();
}
...
}
ShutterButtonListener是一個接口,定義如下:
public class ShutterButton extends RotateImageView implements View.OnLongClickListener {
private static final String TAG = "ShutterButton";
/**
* A callback to be invoked when a ShutterButton's pressed state changes.
*/
public interface OnShutterButtonListener {
/**
* Called when a ShutterButton has been pressed.
*
* @param pressed
* The ShutterButton that was pressed.
*/
void onShutterButtonFocus(ShutterButton button, boolean pressed);
void onShutterButtonClick(ShutterButton button);
void onShutterButtonLongPressed(ShutterButton button);
}
private OnShutterButtonListener mListener;
private boolean mOldPressed;
// M: this variable to avoid needless onClick after onLongPressed;
private boolean mLongPressed;
...
}
它的實(shí)現(xiàn)在CameraAppUiImpl.java中
private OnShutterButtonListener mPhotoShutterListener = new OnShutterButtonListener(...);
private OnShutterButtonListener mVideoShutterListener = new OnShutterButtonListener(...);
public void applayViewCallbacks() {
mShutterManager.setShutterListener(mPhotoShutterListener, mVideoShutterListener,
mCameraActivity.getCameraActor().getOkListener(), mCameraActivity.getCameraActor()
.getCancelListener());
}
第二個InfoManager拓瞪,同樣繼承自ViewManager缴罗,但它相對簡單的多,只是一個TextView的界面
public class InfoManager extends ViewManager {
private static final String TAG = "InfoManager";
private TextView mInfoView;
private CharSequence mInfoText;
public InfoManager(CameraActivity context) {
super(context);
}
@Override
protected View getView() {
View view = inflate(R.layout.onscreen_info);
mInfoView = (TextView) view.findViewById(R.id.info_view);
return view;
}
...
}
onscreen_info.xml
<?xml version="1.0" encoding="utf-8"?>
<com.android.camera.ui.RotateLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/on_screen_info"
style="@style/OnScreenInfoLayout">
<LinearLayout style="@style/OnScreenInfoGroup">
<TextView android:id="@+id/info_view"
style="@style/OnScreenInfoText"/>
</LinearLayout>
</com.android.camera.ui.RotateLayout>
第三個RotateProgress同樣繼承自ViewManager祭埂,也很簡單面氓,界面上顯示為正在保存
public class RotateProgress extends ViewManager {
@SuppressWarnings("unused")
private static final String TAG = "RotateProgress";
private ProgressBar mRotateDialogSpinner;
private TextView mRotateDialogText;
private String mMessage;
public RotateProgress(CameraActivity context) {
super(context, VIEW_LAYER_OVERLAY);
}
@Override
protected View getView() {
View v = getContext().inflate(R.layout.rotate_progress, getViewLayer());
mRotateDialogSpinner = (ProgressBar) v.findViewById(R.id.rotate_dialog_spinner);
mRotateDialogText = (TextView) v.findViewById(R.id.rotate_dialog_text);
return v;
}
...
}
第四個RemainingManager,它也只是一個顯示剩余空間的textview沟堡,在某些場景下它將短暫顯示侧但,同樣繼承自ViewManager,但由于其顯示與CameraActivity生命周期有關(guān)航罗,因此同時將實(shí)現(xiàn)CameraActivity的兩個接口:
public class RemainingManager extends ViewManager implements
CameraActivity.Resumable, CameraActivity.OnParametersReadyListener {
...
@Override
public void begin() {
if (mWorkerHandler == null) {
HandlerThread t = new HandlerThread("thumbnail-creation-thread");
t.start();
mWorkerHandler = new WorkerHandler(t.getLooper());
mWorkerHandler.sendEmptyMessage(MSG_UPDATE_STORAGE);
}
}
@Override
public void resume() {
Log.d(TAG, "resume()");
mResumed = true;
showHint();
}
@Override
public void pause() {
Log.d(TAG, "pause()");
mResumed = false;
if (mStorageHint != null) {
mStorageHint.cancel();
mStorageHint = null;
}
}
@Override
public void finish() {
if (mWorkerHandler != null) {
mWorkerHandler.getLooper().quit();
}
}
@Override
public void onCameraParameterReady() {
mParametersReady = true;
}
...
}
第五個PickerManager禀横,同樣繼承自ViewManager,并實(shí)現(xiàn)了CameraActivity內(nèi)定義的兩個接口粥血,其內(nèi)同時又?jǐn)U展的自己的接口柏锄,它在界面上顯示為相機(jī)右下方豎排多個場景模式的切換
public class PickerManager extends ViewManager implements Listener,
CameraActivity.OnPreferenceReadyListener, CameraActivity.OnParametersReadyListener {
private static final String TAG = "PickerManager";
public interface PickerListener {
boolean onSlowMotionPicked(String turnon);
boolean onHdrPicked(String value);
boolean onGesturePicked(String value);
boolean onSmilePicked(String value);
boolean onCameraPicked(int camerId);
boolean onFlashPicked(String flashMode);
boolean onStereoPicked(boolean stereoType);
boolean onModePicked(int mode, String value, ListPreference preference);
}
private PickerButton mSlowMotion;
private PickerButton mGestureShot;
private PickerButton mHdr;
private PickerButton mSmileShot;
private PickerButton mFlashPicker;
private PickerButton mCameraPicker;
private PickerButton mStereoPicker;
private PickerListener mListener;
private boolean mPreferenceReady;
private CameraActivity mContext;
...
@Override
public void onPreferenceReady() {
Log.i(TAG, "onPreferenceReady()");
mPreferenceReady = true;
}
@Override
public void onCameraParameterReady() {
Log.i(TAG, "onCameraParameterReady(), mDefineOrder:" + mDefineOrder + "" +
", mPreferenceReady:" + mPreferenceReady);
if (!mPreferenceReady) {
return;
}
// the max number of button shown on PickerManager UI is 4, Slow motion,
// hdr, flash, dual camera,
// stereo camera have high priority, gesture, smile have low priority,
// but gesture's priority is
// higher than smile, if the order of button is definite, do not
// redefine again.
if (!mDefineOrder) {
int count = 0;
for (int i = 0; i < mButtonPriority.length; i++) {
ListPreference pref = null;
boolean visible = false;
int buttonIndex = mButtonPriority[i];
switch (buttonIndex) {
case BUTTON_SLOW_MOTION:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_SLOW_MOTION);
break;
case BUTTON_HDR:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_HDR);
break;
case BUTTON_FLASH:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_FLASH);
break;
case BUTTON_CAMERA:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_DUAL_CAMERA);
visible = ModeChecker.getCameraPickerVisible(getContext());
if (visible) {
count++;
if (pref != null) {
pref.showInSetting(false);
}
}
pref = null;
break;
case BUTTON_STEREO:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_STEREO_MODE);
visible = ModeChecker.getStereoPickerVisibile(getContext());
if (visible) {
count++;
if (pref != null) {
pref.showInSetting(false);
}
}
pref = null;
break;
case BUTTON_GESTURE_SHOT:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_GESTURE_SHOT);
break;
case BUTTON_SMILE_SHOT:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_SMILE_SHOT);
break;
default:
break;
}
if (pref != null && pref.getEntries() != null
&& pref.getEntries().length > 1) {
pref.showInSetting(false);
count++;
if (BUTTON_GESTURE_SHOT == buttonIndex) {
sShownStatusRecorder[BUTTON_GESTURE_SHOT] = false;
} else if (BUTTON_SMILE_SHOT == buttonIndex) {
sShownStatusRecorder[BUTTON_SMILE_SHOT] = false;
}
}
Log.i(TAG, "count:" + count + ", buttonIndex:" + buttonIndex);
if (count >= MAX_NUM_OF_SHOWEN) {
break;
}
}
mDefineOrder = true;
} else {
for (int i = 0; i < mButtonPriority.length; i++) {
ListPreference pref = null;
int buttonIndex = mButtonPriority[i];
switch (buttonIndex) {
case BUTTON_SLOW_MOTION:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_SLOW_MOTION);
break;
case BUTTON_HDR:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_HDR);
break;
case BUTTON_FLASH:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_FLASH);
break;
case BUTTON_CAMERA:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_DUAL_CAMERA);
break;
case BUTTON_STEREO:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_STEREO_MODE);
break;
case BUTTON_GESTURE_SHOT:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_GESTURE_SHOT);
break;
case BUTTON_SMILE_SHOT:
pref = (IconListPreference) getContext().getListPreference(
SettingConstants.ROW_SETTING_SMILE_SHOT);
break;
default:
break;
}
if (pref != null) {
pref.showInSetting(sShownStatusRecorder[buttonIndex]);
}
}
}
refresh();
}
...
}
它的接口如何實(shí)現(xiàn)呢,我們回到CameraActivity
private PickerManager.PickerListener mPickerListener = new PickerManager.PickerListener(...);
// Here should be lightweight functions!!!
private void initializeCommonManagers() {
mModePicker = new ModePicker(this);
mFileSaver = new FileSaver(this);
mFrameManager = new FrameManager(this);
mModePicker.setListener(mModeChangedListener);
mCameraAppUi.setSettingListener(mSettingListener);
mCameraAppUi.setPickerListener(mPickerListener);
mCameraAppUi.addFileSaver(mFileSaver);
mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
Log.v(TAG, "getSystemService,mPowerManager =" + mPowerManager);
// For tablet
if (FeatureSwitcher.isSubSettingEnabled()) {
mCameraAppUi.setSubSettingListener(mSettingListener);
}
}
第六個IndicatorManager复亏,暫不清楚是什么
public class IndicatorManager extends ViewManager implements
CameraActivity.OnParametersReadyListener, CameraActivity.OnPreferenceReadyListener {
...
@Override
protected View getView() {
View view = inflate(R.layout.onscreen_indicators);
for (int i = 0; i < INDICATOR_COUNT; i++) {
mViews[i] = (RotateImageView) view.findViewById(VIEW_IDS[i]);
}
mIndicatorGroup = view.findViewById(R.id.on_screen_group);
return view;
}
public void onPreferenceReady() {
for (int i = 0; i < INDICATOR_COUNT; i++) {
String key = SETTING_KEYS[i];
mPrefs[i] = getContext().getListPreference(key);
mDefaults[i] = getContext().getISettingCtrl().getDefaultValue(key);
}
mPreferenceReady = true;
}
public void onCameraParameterReady() {
refreshModeIndicator(true);
refresh();
}
...
}
第七個ReviewManager趾娃,暫不確定是什么東西
public class ReviewManager extends ViewManager implements View.OnClickListener {
private OnClickListener mRetakeLisenter;
private OnClickListener mPlayListener;
...
@Override
public void onClick(View view) {
Log.i(TAG, "onClick, view = " + view);
OnClickListener listener = null;
if (mRetakeView == view) {
// listener = getContext().getCameraActor().getRetakeListener();
listener = mRetakeLisenter;
} else {
// listener = getContext().getCameraActor().getPlayListener();
listener = mPlayListener;
}
// press cancel button will delete the file
// press ok button will send intent to review the file
// if press cancel button and ok button quickly, the error will occurs
if (listener != null && view.isShown()) {
listener.onClick(view);
}
if (LOG) {
Log.d(TAG, "onClick(" + view + ") listener=" + listener);
}
}
...
}
onClick最后響應(yīng)的是RetakeListener或者PlayListener的onClick方法,具體來說在PhotoActor和VideoMode中都有其實(shí)現(xiàn):
PhotoActor.java
public PhotoActor(CameraActivity context, ModuleManager moduleManager, int mode) {
super(context);
...
mICameraAppUi.setReviewListener(mRetakeListener, null);
...
}
private OnClickListener mRetakeListener = new OnClickListener() {
@Override
public void onClick(View view) {
if (mIsCameraClosed) {
Log.i(TAG, "[onClick]mIsCameraClosed = " + mIsCameraClosed);
return;
}
mICameraAppUi.hideReview();
mICameraAppUi.switchShutterType(ShutterButtonType.SHUTTER_TYPE_PHOTO);
restartPreview(true);
}
};
VideoMode.java
private OnClickListener mReviewPlayListener = new OnClickListener() {
public void onClick(View v) {
Log.i(TAG, "[mReviewPlayListener],onClick");
mVideoModeHelper.startPlayVideoActivity(mCurrentVideoUri, mProfile);
}
};
private OnClickListener mRetakeListener = new OnClickListener() {
public void onClick(View v) {
Log.i(TAG, "[mRetakeListener],onClick");
deleteCurrentVideo();
mICameraAppUi.hideReview();
mICameraAppUi.setVideoShutterEnabled(true);
mICameraAppUi.switchShutterType(ShutterButtonType.SHUTTER_TYPE_VIDEO);
}
};
private void showAlert() {
...
mICameraAppUi.setReviewListener(mRetakeListener, mReviewPlayListener);
...
}
第八個RotateDialog缔御,就是一個Dialog抬闷,注意它的show方法的參數(shù)即可
public class RotateDialog extends ViewManager {
...
public void showAlertDialog(String title, String msg, String button1Text, final Runnable r1,
String button2Text, final Runnable r2) {
resetValues();
mTitle = title;
mMessage = msg;
mButton1 = button1Text;
mButton2 = button2Text;
mRunnable1 = r1;
mRunnable2 = r2;
show();
}
...
}
第九個ZoomManager,一個進(jìn)行縮放的視圖耕突,必然涉及手勢監(jiān)聽的問題笤成,如下:
public class ZoomManager extends ViewManager implements
CameraActivity.Resumable, GestureDispatcher.GestureDispatcherListener {
...
@Override
protected View getView() {
return null;
}
@Override
public void begin() {
}
@Override
public void resume() {
mCameraActivity.setGestureDispatcherListener(this);
Log.i(TAG, "resume()");
}
@Override
public void pause() {
mCameraActivity.setGestureDispatcherListener(null);
Log.i(TAG, "pause()");
}
@Override
public void finish() {
}
@Override
public boolean onDown(float x, float y, int width, int height) {
Log.i(TAG, "onDown(" + x + ", " + y + ")");
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.i(TAG, "[onFling] (" + velocityX + ", " + velocityY + ")");
return false;
}
@Override
public boolean onScroll(float dx, float dy, float totalX, float totalY) {
Log.i(TAG, "onScroll(" + dx + ", " + dy + ", " + totalX + ", " + totalY + ")");
return false;
}
@Override
public boolean onSingleTapUp(float x, float y) {
Log.i(TAG, "[onSingleTapUp] (" + x + ", " + y + ")");
return false;
}
@Override
public boolean onSingleTapConfirmed(float x, float y) {
return false;
}
@Override
public boolean onUp() {
Log.i(TAG, "onUp");
return false;
}
@Override
public boolean onDoubleTap(float x, float y) {
if (!FeatureSwitcher.isSupportDoubleTapUp())
return false;
Log.i(TAG, "onDoubleTap(" + x + ", " + y + ") mZoomIndexFactor=" + mZoomIndexFactor
+ ", isAppSupported()=" + isAppSupported() + ", isEnabled()=" + isEnabled());
if (!isAppSupported() || !isEnabled()) {
return false;
}
int oldIndex = findZoomIndex(mLastZoomRatio);
int zoomIndex = 0;
if (oldIndex == 0) {
zoomIndex = getMaxZoomIndex();
mZoomIndexFactor = getMaxZoomIndexFactor();
} else {
mZoomIndexFactor = ZERO;
}
performZoom(zoomIndex, true);
return true;
}
@Override
public boolean onScale(float focusX, float focusY, float scale) {
Log.i(TAG, "onScale(" + focusX + ", " + focusY + ", " + scale + ") mZoomIndexFactor="
+ mZoomIndexFactor + ", isAppSupported()=" + isAppSupported()
+ ", isEnabled()=" + isEnabled());
if (!isAppSupported() || !isEnabled()) {
return false;
}
if (Float.isNaN(scale) || Float.isInfinite(scale)) {
return false;
}
mZoomIndexFactor *= scale;
if (mZoomIndexFactor <= ZERO) {
mZoomIndexFactor = ZERO;
} else if (mZoomIndexFactor >= getMaxZoomIndexFactor()) {
mZoomIndexFactor = getMaxZoomIndexFactor();
}
int zoomIndex = findZoomIndex(Math.round(mZoomIndexFactor * RATIO_FACTOR_RATE));
performZoom(zoomIndex, true);
Log.i(TAG, "onScale() mZoomIndexFactor=" + mZoomIndexFactor);
return true;
}
@Override
public boolean onScaleBegin(float focusX, float focusY) {
Log.i(TAG, "onScaleBegin(" + focusX + ", " + focusY + ")");
return true;
}
@Override
public boolean onLongPress(float x, float y) {
return false;
}
...
}
這里有必要說明一下,它實(shí)現(xiàn)了GestureDispatcherListener這個接口眷茁,而這個接口在什么時候調(diào)用的呢仆邓?GestureRecognizer.Listener這個接口中文判。
public class GestureDispatcher implements GestureRecognizer.Listener,
CameraActivity.OnOrientationListener{
...
public interface GestureDispatcherListener {
public boolean onDown(float x, float y, int width, int height);
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
public boolean onScroll(float dx, float dy, float totalX, float totalY);
public boolean onSingleTapUp(float x, float y);
public boolean onSingleTapConfirmed(float x, float y);
public boolean onUp();
public boolean onDoubleTap(float x, float y);
public boolean onScale(float focusX, float focusY, float scale);
public boolean onScaleBegin(float focusX, float focusY);
public boolean onLongPress(float x, float y);
}
...
}
public class GestureRecognizer {
@SuppressWarnings("unused")
private static final String TAG = "GestureRecognizer";
public interface Listener {
boolean onSingleTapUp(float x, float y);
boolean onSingleTapConfirmed(float x, float y);
void onLongPress(float x, float y);
boolean onDoubleTap(float x, float y);
boolean onScroll(float dx, float dy, float totalX, float totalY);
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
boolean onScaleBegin(float focusX, float focusY);
boolean onScale(float focusX, float focusY, float scale);
void onScaleEnd();
void onDown(float x, float y);
void onUp();
}
public GestureRecognizer(Context context, Listener listener) {
mListener = listener;
Log.i(TAG, "GestureRecognizer");
mGestureDetector = new GestureDetector(context, new MyGestureListener(),
null, true /* ignoreMultitouch */);
mScaleDetector = new ScaleGestureDetector(
context, new MyScaleListener());
mDownUpDetector = new DownUpDetector(new MyDownUpListener());
mListenerAvaliable = true;
}
private class MyScaleListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener {...};
...
}
而這個Listener接口又是在什么時候調(diào)用的呢辕翰? GestureRecognizer中有一個內(nèi)部類MyScaleListener浪藻,它實(shí)現(xiàn)了android.view.ScaleGestureDetector.SimpleOnScaleGestureListener這個接口,在此接口的實(shí)現(xiàn)中調(diào)用了Listener接口的相關(guān)方法登刺。其初始化在CameraActivity的initializeForOpeningProcess()方法中
if (mGestureDispatcher == null) {
mGestureDispatcher = new GestureDispatcher(this);
mGestureRecognizer = new GestureRecognizer(this, mGestureDispatcher);
}
第十個ThumbnailViewManager籽腕,它在相機(jī)的右下角,用于顯示拍照后的小圖窗口
public class ThumbnailViewManager extends ViewManager implements OnClickListener,
FileSaver.FileSaverListener, CameraActivity.Resumable {
...
@Override
public void begin() {
Log.i(TAG, "[begin]...");
if (mWorkerHandler == null) {
HandlerThread t = new HandlerThread("thumbnail-creation-thread");
t.start();
mWorkerHandler = new WorkerHandler(t.getLooper());
}
// move register broadcast from resume to begin, since when Gallery
// start new activity
// to edit, will not receive this broadcast. And LocalBroadcastManager
// use inside process
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getContext());
manager.registerReceiver(mUpdatePictureReceiver, mUpdatePictureFilter);
}
@Override
public void resume() {
Log.i(TAG, "[resume]mResumed = " + mResumed);
if (!mResumed) {
getContext().registerReceiver(mIpoShutdownReceiver, mIpoShutdownFilter);
if (isShowing() && !mIsSavingThumbnail && !getContext().isSecureCamera()) {
// if the ThumbnailView is not showed, do not get last
// thumbnail.
getLastThumbnail();
}
mResumed = true;
}
}
@Override
public void pause() {
Log.i(TAG, "[pause]mResumed =" + mResumed);
if (mResumed) {
getContext().unregisterReceiver(mIpoShutdownReceiver);
cancelLoadThumbnail();
saveThumbnailToFile();
mResumed = false;
}
mWorkerHandler.sendEmptyMessage(MSG_RELEASE_URI);
}
@Override
public void setEnabled(boolean enabled) {
Log.d(TAG, "[setEnabled]enabled = " + enabled);
super.setEnabled(enabled);
if (mThumbnailView != null) {
mThumbnailView.setEnabled(enabled);
mThumbnailView.setClickable(enabled);
}
}
@Override
public void finish() {
Log.i(TAG, "[finish]...");
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getContext());
manager.unregisterReceiver(mUpdatePictureReceiver);
if (mWorkerHandler != null) {
mWorkerHandler.getLooper().quit();
}
}
@Override
protected View getView() {
View view = inflate(R.layout.thumbnail);
mThumbnailView = (RotateImageView) view.findViewById(R.id.thumbnail);
mThumbnailView.setOnClickListener(this);
mPreviewThumb = (RotateImageView) view.findViewById(R.id.preview_thumb);
return view;
}
@Override
protected void onRefresh() {
Log.i(TAG, "[onRefresh]...");
updateThumbnailView();
}
@Override
public void onFileSaved(SaveRequest request) {
Log.i(TAG, "[onFileSaved]...");
// If current URI is not valid, don't create thumbnail.
if (!request.isIgnoreThumbnail() && request.getUri() != null) {
Log.i(TAG, "[onFileSaved],send MSG_SAVE_THUMBNAIL.");
mCurrentSaveRequest = request;
cancelLoadThumbnail();
mWorkerHandler.removeMessages(MSG_SAVE_THUMBNAIL);
mWorkerHandler.sendEmptyMessage(MSG_SAVE_THUMBNAIL);
}
}
@Override
public void onClick(View v) {
if (getContext().isFullScreen() && getContext().isCameraIdle() && mThumbnail != null) {
Uri uri = getThumbnailUri();
if (uri != null && uri.getPath() != null
&& uri.getPath().isEmpty()) {
Log.d(TAG, "[onClick] cancel. uri: " + uri + ", UriPath: "
+ uri.getPath());
return;
}
Log.i(TAG, "[onClick]call gotoGallery.");
getContext().gotoGallery();
}
}
...
}
第十一個FaceBeautyEntryView
public class FaceBeautyEntryView extends ViewManager implements OnClickListener {...}
第十二個SettingManager塘砸,左下角setting圖標(biāo)顯示节仿,非常重要的設(shè)置項(xiàng),實(shí)現(xiàn)了SettingListLayout的Listener接口掉蔬、CameraActivity的OnPreferenceReadyListener接口與TabHost的OnTabChangeListener接口
public class SettingListLayout extends FrameLayout implements InLineSettingItem.Listener,
AdapterView.OnItemClickListener, OnScrollListener {
@SuppressWarnings("unused")
private static final String TAG = "SettingListLayout";
private Listener mListener;
private ArrayList<ListPreference> mListItem = new ArrayList<ListPreference>();
private ArrayAdapter<ListPreference> mListItemAdapter;
private InLineSettingItem mLastItem;
private ListView mSettingList;
public interface Listener {
void onSettingChanged(SettingListLayout settingList, ListPreference preference);
void onStereoCameraSettingChanged(SettingListLayout settingList, ListPreference preference,
int index, boolean showing);
void onRestorePreferencesClicked();
void onVoiceCommandChanged(int index);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if ((position == mListItem.size() - 1) && (mListener != null)) {
mListener.onRestorePreferencesClicked();
}
}
...
}
public class SettingManager extends ViewManager implements View.OnClickListener,
SettingListLayout.Listener, CameraActivity.OnPreferenceReadyListener, OnTabChangeListener {
private static final String TAG = "SettingManager";
public interface SettingListener {
void onSharedPreferenceChanged(ListPreference preference);
void onRestorePreferencesClicked();
void onSettingContainerShowing(boolean show);
void onVoiceCommandChanged(int index);
void onStereoCameraPreferenceChanged(ListPreference preference, int type);
}
protected SettingListener mListener;
...
}
Manager中定義的接口依然在Activity中實(shí)現(xiàn)
private SettingManager.SettingListener mSettingListener = new SettingManager.SettingListener() {
@Override
public void onSharedPreferenceChanged(ListPreference preference) {
Log.d(TAG, "[onSharedPreferenceChanged]");
if (!isCameraOpened()) {
return;
}
if (preference != null) {
String settingKey = preference.getKey();
String value = preference.getValue();
mISettingCtrl.onSettingChanged(settingKey, value);
}
mCameraDeviceCtrl.applyParameters(false);
}
@Override
public void onRestorePreferencesClicked() {
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.d(TAG, "[onRestorePreferencesClicked.run]");
mIsFromRestore = true;
mCameraActor.onRestoreSettings();
mCameraAppUi.collapseViewManager(true);
mCameraAppUi.resetSettings();
SharedPreferences globalPref = mPreferences.getGlobal();
SharedPreferences.Editor editor = globalPref.edit();
editor.clear();
editor.apply();
SettingUtils.upgradeGlobalPreferences(globalPref,
CameraHolder.instance().getNumberOfCameras());
SettingUtils.writePreferredCameraId(globalPref,
mCameraDeviceCtrl.getCameraId());
int backCameraId = CameraHolder.instance()
.getBackCameraId();
SettingUtils.restorePreferences(
(Context) CameraActivity.this,
getSharePreferences(backCameraId),
mCameraDeviceCtrl.getParametersExt(),
isNonePickIntent());
// restore front camera setting
int frontCameraId = CameraHolder.instance()
.getFrontCameraId();
SettingUtils.restorePreferences(
(Context) CameraActivity.this,
getSharePreferences(frontCameraId),
mCameraDeviceCtrl.getParametersExt(),
isNonePickIntent());
SettingUtils.initialCameraPictureSize(
(Context) CameraActivity.this,
mCameraDeviceCtrl.getParametersExt(),
getSharePreferences());
mISettingCtrl.restoreSetting(backCameraId);
mISettingCtrl.restoreSetting(frontCameraId);
mCameraAppUi.resetZoom();
// we should apply parameters if mode is default too.
int mode = mCameraActor.getMode();
if (ModePicker.MODE_PHOTO == mode || !isNonePickIntent()
|| ModePicker.MODE_PHOTO_SGINLE_3D == mode
|| ModePicker.MODE_PHOTO_3D == mode) {
if (ModePicker.MODE_VIDEO == mode
&& !isNonePickIntent()) {
mISettingCtrl.onSettingChanged(
SettingConstants.KEY_VIDEO, SETTING_ON);
}
mCameraDeviceCtrl.applyParameters(false);
} else {
mModePicker.setModePreference(null);
mModePicker.setCurrentMode(ModePicker.MODE_PHOTO);
}
mIsFromRestore = false;
}
};
mCameraAppUi.showAlertDialog(null,
getString(R.string.confirm_restore_message),
getString(android.R.string.cancel), null,
getString(android.R.string.ok), runnable);
}
@Override
public void onSettingContainerShowing(boolean show) {
mModuleManager.onSettingContainerShowing(show);
if (show) {
} else {
if (isFaceBeautyEnable()
&& getCurrentMode() == ModePicker.MODE_FACE_BEAUTY
&& !mIsFromRestore) {
// when face is detected will show the icon
// otherwise don't show
Log.i(TAG,
"onSettingContainerShowing, will set modify icon stautes true,"
+ "and show FB icon");
// mLomoEffectsManager.show();
}
}
}
@Override
public void onVoiceCommandChanged(int commandId) {
mModuleManager.onVoiceCommandNotify(commandId);
}
@Override
public void onStereoCameraPreferenceChanged(ListPreference preference,
int type) {
if (preference != null
&& preference.getKey().equals(
SettingConstants.KEY_DUAL_CAMERA_MODE)) {
Log.i(TAG, "onStereoCameraPreferenceChanged, type = " + type);
if (getCurrentMode() == ModePicker.MODE_STEREO_CAMERA) {
if (type == DUAL_CAMERA_ENHANCE_ENABLE) {
enableDualCameraExtras();
}
if (type == DUAL_CAMERA_ENHANCE_DISABLE) {
disableDualCameraExtras();
}
if (type == DUAL_CAMERA_START) {
singleDualCameraExtras();
mCameraDeviceCtrl.applyParameters(false);
return;
}
mCameraDeviceCtrl.applyParameters(false);
return;
} else {
if (type == DUAL_CAMERA_ENHANCE_ENABLE) {
enableDualCameraExtras();
}
if (type == DUAL_CAMERA_ENHANCE_DISABLE) {
disableDualCameraExtras();
}
if (type == DUAL_CAMERA_START) {
singleDualCameraExtras();
initializeDualCamera(false);
return;
}
if (type == DUAL_CAMERA_SWITCH_IN_REFOCUS) {
singleDualCameraExtras();
mCameraDeviceCtrl.applyParameters(false);
return;
}
initializeDualCamera(false);
return;
}
}
}
};
接口的調(diào)用過程還是比較清楚的廊宪,從SettingListLayout到SettingManager再到CameraActivity
第十三個EffectViewManager矾瘾,定義了一個接口并在CameraAppUiImpl中使用內(nèi)部類實(shí)現(xiàn),界面上顯示上左邊的一個右箭頭
public class EffectViewManager extends ViewManager implements View.OnClickListener {
private static final String TAG = "EffectViewManager";
private EffectListener mListener;
private RotateImageView mIndicator;
public interface EffectListener {
public boolean onClick();
}
...
}
CameraAppUiImpl.java
private class EffectListenerImpl implements EffectListener {
@Override
public boolean onClick() {
mCameraActivity.getModuleManager().onEffectClick();
return true;
}
}
第十四個SubSettingManager箭启,它繼承自SettingManager壕翩,用于擴(kuò)展多級設(shè)置選項(xiàng)
public class SubSettingManager extends SettingManager {
private static final String TAG = "SubSettingManager";
public SubSettingManager(CameraActivity context) {
super(context);
}
...
}
最后我們看一下這些Manager的父類ViewManager.java
public abstract class ViewManager implements CameraActivity.OnOrientationListener {
private final int mViewLayer;
...
public ViewManager(CameraActivity context, int layer) {
mContext = context;
mContext.addViewManager(this);
mContext.addOnOrientationListener(this);
mOrientation = mContext.getOrientationCompensation();
mViewLayer = layer;
}
public ViewManager(CameraActivity context) {
this(context, VIEW_LAYER_NORMAL);
}
public final View inflate(int layoutId) {
return getContext().inflate(layoutId, mViewLayer);
}
...
}
這個mViewLayer將決定它呈現(xiàn)的具體位置,在它的所有子類中將調(diào)用此inflate方法傅寡,而該方法最后指向了CameraActivity的inflate()方法
public View inflate(int layoutId, int layer) {
return mCameraAppUi.inflate(layoutId, layer);
}
回到CameraAppUiImpl.java
public View inflate(int layoutId, int layer) {
// mViewLayerNormal, mViewLayerBottom and mViewLayerTop are same
// ViewGroup.
// Here just use one to inflate child view.
return mCameraActivity.getLayoutInflater().inflate(layoutId, getViewLayer(layer), false);
}
private ViewGroup getViewLayer(int layer) {
Log.i(TAG, "[getViewLayer] layer:" + layer);
ViewGroup viewLayer = null;
switch (layer) {
case ViewManager.VIEW_LAYER_BOTTOM:
viewLayer = mViewLayerBottom;
break;
case ViewManager.VIEW_LAYER_NORMAL:
viewLayer = mViewLayerNormal;
break;
case ViewManager.VIEW_LAYER_TOP:
viewLayer = mViewLayerTop;
break;
case ViewManager.VIEW_LAYER_SHUTTER:
viewLayer = mViewLayerShutter;
break;
case ViewManager.VIEW_LAYER_SETTING:
viewLayer = mViewLayerSetting;
break;
case ViewManager.VIEW_LAYER_OVERLAY:
viewLayer = mViewLayerOverlay;
break;
default:
throw new RuntimeException("Wrong layer:" + layer);
}
return viewLayer;
}
最終根據(jù)layer值確定managerview填充在哪一層FrameLayout放妈,而具體的位置由managerview所加載的布局文件決定,例如
style="@style/RemainingLayout"
<style name="RemainingLayout" parent="ScreenOnMargin">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_alignParentTop">true</item>
<item name="android:layout_alignParentRight">true</item>
</style>
這樣就確定了RemainingManager最后在屏幕的右上方荐操。