Launcher8.0启动流程的第六步startLoader的第一步Loadworkspace完成,接着是第二步bindworkspace。 这一步将sBgDataModel中的图标放到桌面上。 放置的时候需要先创建屏幕而后才能将图标放到屏幕里。
源码如下:
private void bindWorkspace(int synchronizeBindPage) {
final long t = SystemClock.uptimeMillis();
Runnable r;
//启动流程Launcher调用LauncherModel的方法startLoader,而LauncherModel又会调用Launcher的方法来完成startLoader的操作。在Launcher的启动流程的第三步创建必须的对象中创建的第二个对象是LauncherModel的对象,在创建的时候传入了Launcher本身,而这个Launcher的对象在LauncherMedel类里面叫做Callbacks。随后的方法 final Callbacks oldCallbacks = mCallbacks.get();就是获取Launcher对象。
final Callbacks oldCallbacks = mCallbacks.get();
if (oldCallbacks == null) {
Log.w(TAG, "LoaderTask running with no launcher");
return;
}
//随后将sBgDataModel里面存储的桌面布局信息放到LauncherModel的集合里面,这样方便使用这些信息。一共创建了三个信息,屏幕数,桌面图标,桌面widget。后面将按照屏幕数、桌面图标、桌面widget依次绘制。
ArrayList
ArrayList
ArrayList
synchronized (sBgDataModel) {
workspaceItems.addAll(sBgDataModel.workspaceItems);
appWidgets.addAll(sBgDataModel.appWidgets);
orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
}
//图标信息到位之后,先找到当前屏幕。获取屏幕的id,屏幕的id是0,1,2这个顺序,且严格按照这个顺序。比如Id为1,则必定是从左往右的第2个屏幕。在图标信息iteminfo里面存有每个图标的screenid信息。
final int currentScreen;
{
int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
if (currScreen >= orderedScreenIds.size()) {
// There may be no workspace screens (just hotseat items and an empty page).
currScreen = PagedView.INVALID_RESTORE_PAGE;
}
currentScreen = currScreen;
}
final boolean validFirstPage = currentScreen >= 0;
final long currentScreenId =
validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
//将图标分为在当前屏幕和没有在当前屏幕,且由于widget和其他类型的文件有巨大差异,如内容提供方和占空间大小。所以,widget和其他分为两类。
ArrayList
ArrayList
ArrayList
ArrayList
filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
otherAppWidgets);
//区分是否在当前屏幕的方法filterCurrentWorkspaceItems,通过比较if (info.screenId == currentScreenId) 来确定是否在当前屏幕,源码如下:
private void filterCurrentWorkspaceItems(long currentScreenId,
ArrayList
ArrayList
ArrayList
Iterator
while (iter.hasNext()) {
ItemInfo i = iter.next();
if (i == null) {
iter.remove();
}
}
Set
Collections.sort(allWorkspaceItems, new Comparator
@Override
public int compare(ItemInfo lhs, ItemInfo rhs) {
return Utilities.longCompare(lhs.container, rhs.container);
}
});
for (ItemInfo info : allWorkspaceItems) {
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (info.screenId == currentScreenId) {
currentScreenItems.add(info);
itemsOnScreen.add(info.id);
} else {
otherScreenItems.add(info);
}
} else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
currentScreenItems.add(info);
itemsOnScreen.add(info.id);
} else {
if (itemsOnScreen.contains(info.container)) {
currentScreenItems.add(info);
itemsOnScreen.add(info.id);
} else {
otherScreenItems.add(info);
}
}
}
}
//然后将图标进行整理,将图标从上到下从左到右按顺序排好,因为图标的显示始终是一个一个依次显示,虽然速度很快,但是在手机卡顿的时候,难免第一个图标和最后一个图标还是能被人感知。如果有顺序的显示,用户体验会好很多。
sortWorkspaceItemsSpatially(currentWorkspaceItems);
sortWorkspaceItemsSpatially(otherWorkspaceItems);
//这里调用了Launcher的startBinding方法,google Launcher的习惯先用一个start的方法作为一个实际操作的开始,这里的startBinding会完成resetLayout等清空数据的操作
r = new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.clearPendingBinds();
callbacks.startBinding();
}
}
};
runOnMainThread(r);
//而后是核心代码,首先绑定屏幕,传入的参数是orderedScreenIds,参数源于数据库。
bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
private void bindWorkspaceScreens(final Callbacks oldCallbacks,
final ArrayList
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindScreens(orderedScreens);
}
}
};
runOnMainThread(r);
}
//绑定屏幕采用回调Launcher的bindScreens方法,由于第一屏会有概率出现 google搜索栏所以需要特殊定制,注意定制的google搜索栏不存于数据库中,其具备不可移动不可删除的特性,而google搜索栏在创建时是随着屏幕一同创建。
public void bindScreens(ArrayList
if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
mWorkspace.addExtraEmptyScreen();
}
bindAddScreens(orderedScreenIds);
if (hasCustomContentToLeft()) {
mWorkspace.createCustomContentContainer();
populateCustomContentContainer();
}
mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
}
//对于绑定屏幕实质是创建与数据库中屏幕数一致的空屏幕。
private void bindAddScreens(ArrayList
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
long screenId = orderedScreenIds.get(i);
if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
// No need to bind the first screen, as its always bound.
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
}
}
}
// 创建空屏幕的方法是insertNewWorkspaceScreen,核心方法是addView方法,workspace是由celllayout组成的viewGroup,通过addview往里面添加celllayout也就是添加空屏幕。
public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
if (mWorkspaceScreens.containsKey(screenId)) {
throw new RuntimeException("Screen id " + screenId + " already exists!");
}
CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
newScreen.setSoundEffectsEnabled(false);
mWorkspaceScreens.put(screenId, newScreen);
mScreenOrder.add(insertIndex, screenId);
addView(newScreen, insertIndex);
if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
return newScreen;
}
以上完成了屏幕的添加,随后就需要往屏幕中添加桌面的图标。实际图标screen的子view,所以需要先有屏幕才能有图标。