1. 程式人生 > >Launcher3 應用圖示的載入流程

Launcher3 應用圖示的載入流程

下面從bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,

 currentFolders, null);開始具體分析應用圖示的載入過程:

LauncherModel.java:

private void bindWorkspaceItems(final Callbacks oldCallbacks,

                final ArrayList<ItemInfo> workspaceItems,

                final ArrayList<LauncherAppWidgetInfo> appWidgets,

                final HashMap<Long, FolderInfo> folders,

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

                   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);對子物件進行佈局。