Launcher3中Folder的一點分析

現在市面上大多數手機的桌面文件夾都是類似ios那樣的風格,將內部包含的app縮略展示在icon圖標上;但是Google原生的文件夾風格是圓形背景,app以45度角向外疊起來的。據說Android早期版本也是ios那樣的,後來被蘋果告侵權才改的。不管怎麼說,我個人還是覺得縮略圖展示是比較好的,方便查看內部包含的app。正好這段時間比較閒,就來改的玩玩。

以下只是對Launcher3中Folder的源碼作了分析,看懂了源碼,想要改風格是非常容易的。

Folder的創建起源於Workspace.java的onDrop回調,每次onDrop都需要判斷是否需要創建文件夾。添加文件夾時,首先要將targetCell位置的app從CellLayout中移除,然後再調用Launcher.java的addFolder,將創建好的FolderIcon添加回targetCell這個位置;最後清除放入文件夾的兩個app的座標(因爲他們被移出了workspace),並將這兩個app添加到FolderIcon中。

boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
            int[] targetCell, float distance, boolean external, DragView dragView,
            Runnable postAnimationRunnable) {
                            …………
            target.removeView(v);

            FolderIcon fi =
                mLauncher.addFolder(target, container, screenId, targetCell[0], targetCell[1]);
            destInfo.cellX = -1;
            destInfo.cellY = -1;
            sourceInfo.cellX = -1;
            sourceInfo.cellY = -1;

            // If the dragView is null, we can't animate
            boolean animate = dragView != null;
            if (animate) {
                fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
                        postAnimationRunnable);
            } else {
                fi.addItem(destInfo);
                fi.addItem(sourceInfo);
            }
            return true;
        }
        return false;
    }

在Launcher.java的addFolder中創建了2個實例,folderInfo和newFolder,並通過Workspace的addInScreen將foler添加到屏幕上。

FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
            int cellY) {
        final FolderInfo folderInfo = new FolderInfo();
        folderInfo.title = getText(R.string.folder_name);

        // Update the model
        LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
                cellX, cellY);
        sFolders.put(folderInfo.id, folderInfo);

        // Create the view
        FolderIcon newFolder =
            FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
        mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
                isWorkspaceLocked());
        // Force measure the new folder icon
        CellLayout parent = mWorkspace.getParentCellLayoutForView(newFolder);
        parent.getShortcutsAndWidgets().measureChild(newFolder);
        return newFolder;
    }

FolderIcon實際上是一個自定義View,繼承自FrameLayout,其核心改寫在dispatchDraw。爲什麼不是onDraw?因爲FrameLayout是ViewGroup啊。

@Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);

        if (mFolder == null) return;
        if (mFolder.getItemCount() == 0 && !mAnimating) return;

        ArrayList<View> items = mFolder.getItemsInReadingOrder();
        Drawable d;
        TextView v;

        // Update our drawing parameters if necessary
        if (mAnimating) {
            computePreviewDrawingParams(mAnimParams.drawable);
        } else {
            v = (TextView) items.get(0);
            d = getTopDrawable(v);
            computePreviewDrawingParams(d);
        }

        int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
        if (!mAnimating) {
            for (int i = nItemsInPreview - 1; i >= 0; i--) {
                v = (TextView) items.get(i);
                if (!mHiddenItems.contains(v.getTag())) {
                    d = getTopDrawable(v);
                    mParams = computePreviewItemDrawingParams(i, mParams);
                    mParams.drawable = d;
                    drawPreviewItem(canvas, mParams);
                }
            }
        } else {
            drawPreviewItem(canvas, mAnimParams);
        }
    }

DispatchDraw包含三部分:
computePreviewDrawingParams:計算背景參數
computePreviewItemDrawingParams:計算內部Item參數,主要是計算座標,縮放尺寸,透明度。
drawPreviewItem:依據參數繪製Icon內的Item。

因此要修改Folder樣式,主要就是修改computePreviewItemDrawingParams的邏輯,還是挺簡單的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章