android launcher開發(3)初始化介面
阿新 • • 發佈:2019-02-05
初始化執行環境
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
在oncreate中會呼叫這個方法,LauncherAppState 儲存著初始化的資訊,並且Launcher實現了LauncherProviderChangeListener這個介面,然後兩者產生聯絡,當應用的快捷方式,資料夾發生了變換,資料庫也就發生了變換,進而通知LauncherAppState 。 ## Launcher與launchermode的連線 ##
mModel = app.setLauncher(this);
launcher實現了launchermodel的callbacks介面。通過這樣的呼叫,launchermodel’生成的資料就可以傳遞給launcher了。
launcher oncreate方法
super.onCreate(savedInstanceState);
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
// Load configuration-specific DeviceProfile
mDeviceProfile = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE ?
app.getInvariantDeviceProfile().landscapeProfile
: app.getInvariantDeviceProfile().portraitProfile;
//設定有多少個格子
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this );
mIconCache = app.getIconCache();
//圖示緩衝區
mDragController = new DragController(this);
//初始化拖拽器,這個可能是我們最經常的操作了
mInflater = getLayoutInflater();
mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
mStats = new Stats(this);
//launcher中有一個stats.log檔案,儲存著應用的啟動資訊。在stats類例項化的時候,會讀取檔案的資訊,並進行儲存
/*
* 桌面小部件
* 通過LauncherAppWidgetHost載入小部件,並啟動監聽
* */
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
mPaused = false;
if (PROFILE_STARTUP) {
android.os.Debug.startMethodTracing(
Environment.getExternalStorageDirectory() + "/launcher");
}
setContentView(R.layout.launcher);
//設定佈局檔案,由此可見,launcher其實也是一個activity
setupViews();
// 對控制元件初始化
mDeviceProfile.layout(this);
/*
* 上一個我們討論了activity的狀態恢復
* 這裡進行的是laucher的狀態恢復
* */
mSavedState = savedInstanceState;
restoreState(mSavedState);
/*
* launchermodel 負責launcher的資料處理
* mrestoring 是否是資料的恢復處理
* */
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
/*
* launchermodel 負責launcher的資料處理
* mrestoring 是否是資料的恢復處理
* */
if (!mRestoring) {
if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
} else {
// We only load the page synchronously if the user rotates (or triggers a
// configuration change) while launcher is in the foreground
mModel.startLoader(mWorkspace.getRestorePage());
}
}
// For handling default keys
mDefaultKeySsb = new SpannableStringBuilder();
Selection.setSelection(mDefaultKeySsb, 0);
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
//配置檔案規定了launcher的的方向,點進去我們可以看到sForceEnableRotation,allow_rotation這兩個引數有關
mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
// 但是這裡,如果說我們的引數設定了false,那麼我們依然後判斷共享引數,如果共享引數中儲存可以
// 任意方向旋轉,那麼依然成立
if (!mRotationEnabled) {
mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false);
}
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
// we want the screen to auto-rotate based on the current orientation
// mRotationEnabled設定launcher的旋轉方向
setOrientation();
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
if (mLauncherCallbacks.hasLauncherOverlay()) {
ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub);
mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate();
mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView(
mLauncherOverlayContainer, mLauncherOverlayCallbacks);
mWorkspace.setLauncherOverlay(mLauncherOverlay);
}
}
/*
* 是否需要顯示介紹介面
* shouldShowIntroScreen()判斷是否顯示,獲取共享引數的值
*
* */
if (shouldShowIntroScreen()) {
showIntroScreen();
} else {
// 如果不需要顯示介紹介面,那麼launcher考慮是否在第一次啟動的時候顯示幫助介面,同樣的這裡也是參考了配置檔案的資料
showFirstRunActivity();
showFirstRunClings();
}
}
控制元件初始化
private void setupViews() {
final DragController dragController = mDragController;
mLauncherView = findViewById(R.id.launcher);
mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
mWorkspace.setPageSwitchListener(this);
// 設定workspace切換監聽器
mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
//這個是指示器
mLauncherView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
/*
* 設定launcher為全屏
* 獲得背景圖p
* */
// Setup the drag layer
mDragLayer.setup(this, dragController);
//設定拖拽曾
// Setup the hotseat
mHotseat = (Hotseat) findViewById(R.id.hotseat);
// 快捷啟動欄
if (mHotseat != null) {
mHotseat.setOnLongClickListener(this);
}
mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
mWidgetsButton = findViewById(R.id.widget_button);
mWidgetsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
onClickAddWidgetButton(arg0);
}
}
});
mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
View wallpaperButton = findViewById(R.id.wallpaper_button);
wallpaperButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
onClickWallpaperPicker(arg0);
}
}
});
wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
View settingsButton = findViewById(R.id.settings_button);
if (hasSettings()) {
settingsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
onClickSettingsButton(arg0);
}
}
});
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
} else {
settingsButton.setVisibility(View.GONE);
}
mOverviewPanel.setAlpha(0f);
/*
* 這一塊是對預覽模式下的設定,長按螢幕會進入預覽模式
* 下面會有三個按鈕,桌布,小部件,設定
*
* */
// Setup the workspace應用選單誤操作反饋
mWorkspace.setHapticFeedbackEnabled(false);
mWorkspace.setOnLongClickListener(this);//設定長按
// 接收控制器的控制
mWorkspace.setup(dragController);
dragController.addDragListener(mWorkspace);
// Get the search/delete bar。獲取搜尋框例項
mSearchDropTargetBar = (SearchDropTargetBar)
mDragLayer.findViewById(R.id.search_drop_target_bar);
// Setup Apps and Widgets,初始化小部件,常用app
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
} else {
mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
dragController.setScrollView(mDragLayer);
dragController.setMoveTarget(mWorkspace);
dragController.addDropTarget(mWorkspace);
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.setup(this, dragController);
mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
}
//初始化小部件 ,讀取檔案資料,如果為true,則進行載入
if (getResources().getBoolean(R.bool.debug_memory_enabled)) {
Log.v(TAG, "adding WeightWatcher");
// 鐘錶元件
mWeightWatcher = new WeightWatcher(this);
mWeightWatcher.setAlpha(0.5f);
((FrameLayout) mLauncherView).addView(mWeightWatcher,
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM)
);
boolean show = shouldShowWeightWatcher();
mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
}
}
mDeviceProfile.layout(this);
/*
* 控制元件已經進行了初始化,那麼怎麼擺放呢
* launcher中含有搜尋框,hotseat快捷啟動欄,頁面指示器,那麼我們就必須呼叫DeviceProfile的layout方法設定其位置,內容比較繁雜,但都是一個道理,根據橫屏豎屏,以及引數改變其位置
* */
public void layout(Launcher launcher) {
FrameLayout.LayoutParams lp;
/*
* 佈局引數ip
* 判斷是否是豎屏
* */
boolean hasVerticalBarLayout = isVerticalBarLayout();
final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
// Layout the search bar space
View searchBar = launcher.getSearchDropTargetBar();
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
if (hasVerticalBarLayout) {
// Vertical search bar space -- The search bar is fixed in the layout to be on the left
// of the screen regardless of RTL
lp.gravity = Gravity.LEFT;
lp.width = searchBarSpaceHeightPx;
LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
targets.setOrientation(LinearLayout.VERTICAL);
FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
targetsLp.gravity = Gravity.TOP;
targetsLp.height = LayoutParams.WRAP_CONTENT;
} else {
// Horizontal search bar space
lp.gravity = Gravity.TOP;
lp.height = searchBarSpaceHeightPx;
LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
targets.getLayoutParams().width = searchBarSpaceWidthPx;
}
searchBar.setLayoutParams(lp);
// Layout the workspace
PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
lp.gravity = Gravity.CENTER;
Rect padding = getWorkspacePadding(isLayoutRtl);
workspace.setLayoutParams(lp);
workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl));
// Layout the hotseat
// 獲取hotseat例項
View hotseat = launcher.findViewById(R.id.hotseat);
// 獲得LayoutParams
lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
if (hasVerticalBarLayout) {
// Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
// screen regardless of RTL
lp.gravity = Gravity.RIGHT;
lp.width = hotseatBarHeightPx;
lp.height = LayoutParams.MATCH_PARENT;
hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
} else if (isTablet) {
// Pad the hotseat with the workspace padding calculated above
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
lp.height = hotseatBarHeightPx;
hotseat.setPadding(edgeMarginPx + padding.left, 0,
edgeMarginPx + padding.right,
2 * edgeMarginPx);
} else {
/*
* 設定快鍵攔的位置為底部,設定padding距離
* */
// For phones, layout the hotseat without any bottom margin
// to ensure that we have space for the folders
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
lp.height = hotseatBarHeightPx;
hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
2 * edgeMarginPx, 0);
}
hotseat.setLayoutParams(lp);
launcher狀態恢復
private void restoreState(Bundle savedState) {
// 判斷savestate資料是否為空
if (savedState == null) {
return;
}
State state = intToState(savedState.getInt(RUNTIME_STATE, State.WORKSPACE.ordinal()));
if (state == State.APPS || state == State.WIDGETS) {
mOnResumeState = state;
}
//這個跟我們上次所講的儲存狀態發生關聯
int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN,
PagedView.INVALID_RESTORE_PAGE);
// 獲得應用程式選單在第幾頁,然後進行恢復
if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
mWorkspace.setRestorePage(currentScreen);
}
//恢復要新增的元件
final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
mPendingAddInfo.container = pendingAddContainer;
mPendingAddInfo.screenId = pendingAddScreen;
mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
AppWidgetProviderInfo info = savedState.getParcelable(
RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
mPendingAddWidgetInfo = info == null ?
null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info);
mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
// 根據 儲存的資訊進行恢復,使launcher處於等待狀態,下面貼出列這個方法的實現,似乎只是把value的值付給了mwaitingforresult,然後進行了判斷,兩者不可能不一樣,那麼呼叫 onWorkspaceLockedChanged();
setWaitingForResult(true);
// 這個應該表示恢復成功,進行標識,以便後續使用
mRestoring = true;
}
mItemIdToViewId = (HashMap<Integer, Integer>)
savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
}
private void setWaitingForResult(boolean value) {
boolean isLocked = isWorkspaceLocked();
mWaitingForResult = value;
if (isLocked != isWorkspaceLocked()) {
onWorkspaceLockedChanged();
}
}
showIntroScreen介紹介面的顯示
rotected void showIntroScreen() {
// 獲取介紹介面的view
View introScreen = getIntroScreen();
// 設定介紹介面的桌布不可見
changeWallpaperVisiblity(false);
if (introScreen != null) {
// 這個在我們初始化控制元件的時候有顯示 draglayer是一個根檢視,在內部進行了一個addview的操作,這個
// 我們會很熟悉
mDragLayer.showOverlayView(introScreen);
}
if (mLauncherOverlayContainer != null) {
mLauncherOverlayContainer.setVisibility(View.INVISIBLE);
}
}
到這裡launcher3的oncreate方法介紹完畢。 跟我們尋常的activity一樣,他主要進行控制元件的初始化,以及獲得設定的引數,做出相應的處理。