Launcher3 应用图标的加载流程

下面从bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,

 currentFolders, null);开始具体分析应用图标的加载过程:

 

LauncherModel.java:

private void bindWorkspaceItems(final Callbacks oldCallbacks,

                final ArrayList workspaceItems,

                final ArrayList appWidgets,

                final HashMap folders,

                ArrayList deferredBindRunnables) {

 

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 shortcuts, final int start,

                            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 bounceAnims = new ArrayList();

                   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()函数,最终根据参数lpshortcut添加到容器中。

 

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);对子对象进行布局。

你可能感兴趣的:(Launcher3 应用图标的加载流程)