下面从bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
currentFolders, null);开始具体分析应用图标的加载过程:
LauncherModel.java:
private void bindWorkspaceItems(final Callbacks oldCallbacks,
final ArrayList
final ArrayList
final HashMap
ArrayList
for (int i = 0; i < N; i += ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
final Runnable r = new Runnable() {
@Override
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindItems(workspaceItems, start, start+chunkSize,
false);
}
}
};
if (postOnMainThread) {
deferredBindRunnables.add(r);
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
}
}
在上述代码中,callbacks.bindItems(workspaceItems, start, start+chunkSize, false);是加载的核心代码:
Launcher.java:
public void bindItems(final ArrayList
final int end, final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
public void run() {
bindItems(shortcuts, start, end, forceAnimateIcons);
}
};
if (waitUntilResume(r)) {
return;
}
// Get the list of added shortcuts and intersect them with the set of
// shortcuts here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection
final boolean animateIcons = forceAnimateIcons
&& canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
// Short circuit if we are loading dock items for a configuration
// which has no dock
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
&& mHotseat == null) {
continue;
}
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
ShortcutInfo info = (ShortcutInfo) item;
View shortcut = createShortcut(info);
/*
* TODO: FIX collision case
*/
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
throw new RuntimeException("OCCUPIED");
}
}
workspace.addInScreenFromBind(shortcut, item.container,
item.screenId, item.cellX, item.cellY, 1, 1);
if (animateIcons) {
// Animate all the applications up now
shortcut.setAlpha(0f);
shortcut.setScaleX(0f);
shortcut.setScaleY(0f);
bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
newShortcutsScreenId = item.screenId;
}
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon,
this, (ViewGroup) workspace.getChildAt(workspace
.getCurrentPage()), (FolderInfo) item,
mIconCache);
workspace.addInScreenFromBind(newFolder, item.container,
item.screenId, item.cellX, item.cellY, 1, 1);
break;
default:
throw new RuntimeException("Invalid Item Type");
}
}
if (animateIcons) {
// Animate to the correct page
if (newShortcutsScreenId > -1) {
long currentScreenId = mWorkspace
.getScreenIdForPageIndex(mWorkspace.getNextPage());
final int newScreenIndex = mWorkspace
.getPageIndexForScreenId(newShortcutsScreenId);
final Runnable startBounceAnimRunnable = new Runnable() {
public void run() {
anim.playTogether(bounceAnims);
anim.start();
}
};
if (newShortcutsScreenId != currentScreenId) {
// We post the animation slightly delayed to prevent
// slowdowns
// when we are loading right after we return to launcher.
mWorkspace.postDelayed(new Runnable() {
public void run() {
if (mWorkspace != null) {
mWorkspace.snapToPage(newScreenIndex);
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
}
}
}, NEW_APPS_PAGE_MOVE_DELAY);
} else {
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
}
}
}
workspace.requestLayout();
}
上述代码中,以下两行代码创建新的应用图标的快捷方式。
ShortcutInfo info = (ShortcutInfo) item;
View shortcut = createShortcut(info);
以下代码是把快捷方式添加到屏幕中对应位置:
workspace.addInScreenFromBind(shortcut, item.container,
item.screenId, item.cellX, item.cellY, 1, 1);
该函数的实现:
WorkSpace.java:
void addInScreenFromBind(View child, long container, long screenId, int x, int y,
int spanX, int spanY) {
addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
}
被调用函数addInScreen()函数的实现:
WorkSpace.java:
void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
boolean insert, boolean computeXYFromRank) {
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
// DEBUGGING - Print out the stack trace to see where we are adding from
new Throwable().printStackTrace();
return;
}
}
if (screenId == EXTRA_EMPTY_SCREEN_ID) {
// This should never happen
throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
}
final CellLayout layout;
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
layout = mLauncher.getHotseat().getLayout();
child.setOnKeyListener(null);
// Hide folder title in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(false);
}
if (computeXYFromRank) {
x = mLauncher.getHotseat().getCellXFromOrder((int) screenId);
y = mLauncher.getHotseat().getCellYFromOrder((int) screenId);
} else {
screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
}
} else {
// Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(true);
}
layout = getScreenWithId(screenId);
child.setOnKeyListener(new IconKeyEventListener());
}
ViewGroup.LayoutParams genericLp = child.getLayoutParams();
CellLayout.LayoutParams lp;
if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
} else {
lp = (CellLayout.LayoutParams) genericLp;
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}
if (spanX < 0 && spanY < 0) {
lp.isLockedToGrid = false;
}
// Get the canonical child id to uniquely represent this view in this screen
ItemInfo info = (ItemInfo) child.getTag();
int childId = mLauncher.getViewIdForItem(info);
boolean markCellsAsOccupied = !(child instanceof Folder);
if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
// TODO: This branch occurs when the workspace is adding views
// outside of the defined grid
// maybe we should be deleting these items from the LauncherModel?
Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
}
if (!(child instanceof Folder)) {
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(mLongClickListener);
}
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
}
}
该代码中的核心代码是 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {},该函数中的主要参数有child:添加的图标对象,lp:封装了该对象在CellLayout中位置。
addViewToCellLayout()函数的实现:
CellLayout.java:
public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
boolean markCells) {
final LayoutParams lp = params;
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(!mIsHotseat);
}
child.setScaleX(getChildrenScale());
child.setScaleY(getChildrenScale());
// Generate an id for each view, this assumes we have at most 256x256 cells
// per workspace screen
if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
// If the horizontal or vertical span is set to -1, it is taken to
// mean that it spans the extent of the CellLayout
if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
child.setId(childId);
mShortcutsAndWidgets.addView(child, index, lp);
if (markCells) markCellsAsOccupiedForView(child);
return true;
}
return false;
}
该函数实现把Item对象添加到CellLayout中,该函数的核心代码是 mShortcutsAndWidgets.addView(child, index, lp)
mShortcutsAndWidgets对象其实是CellLayout中的最终添加shortcut的容器,调用addView()函数,最终根据参数lp把shortcut添加到容器中。
在ShortcutAndWidgetContainer.java中,主要是调用onMeasure()和onLayout()函数对添加的item进行测量和布局。
ShortcutAndWidgetContainer.java:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSpecSize, heightSpecSize);
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child);
}
}
}
该函数调用measureChild()对子对象进行测量。
measureChild()的实现:
ShortcutAndWidgetContainer.java:
public void measureChild(View child) {
final LauncherAppState app = LauncherAppState.getInstance();
final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
final int cellWidth = mCellWidth;
final int cellHeight = mCellHeight;
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (!lp.isFullscreen) {
lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, invertLayoutHorizontally(), mCountX);
if (child instanceof LauncherAppWidgetHostView) {
// Widgets have their own padding, so skip
} else {
// Otherwise, center the icon
int cHeight = getCellContentHeight();
int cellPaddingY = (int) Math.max(0,
((lp.height - cHeight) / 2f));
int cellPaddingX = (int) (grid.edgeMarginPx / 2f);
cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
}
} else {
lp.x = 0;
lp.y = 0;
lp.width = getMeasuredWidth();
lp.height = getMeasuredHeight();
}
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
上述代码中比较重要的方法有,lp.setup(cellWidth, cellHeight, mWidthGap, mHeightGap, invertLayoutHorizontally(), mCountX),这个方法用于把计算的结果封装到LayoutParams中,用于布局item的时候进行调用。
ShortcutAndWidgetContainer.java:
onLayout()函数:
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
int childLeft = lp.x;
int childTop = lp.y;
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
if (lp.dropped) {
lp.dropped = false;
final int[] cellXY = mTmpCellXY;
getLocationOnScreen(cellXY);
mWallpaperManager.sendWallpaperCommand(getWindowToken(),
WallpaperManager.COMMAND_DROP,
cellXY[0] + childLeft + lp.width / 2,
cellXY[1] + childTop + lp.height / 2, 0, null);
}
}
}
}
该函数主要调用child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);对子对象进行布局。