• Launcher folder、foldericon


    桌面文件夹功能简介
    1、界面介绍

    长按桌面图标会形成文件夹。
    打开的文件夹叫Folder
    桌面上和图标一样的文件夹叫FolderIcon

    文件夹的主要逻辑代码都在Launcher\src\com\android\launcher3\folder包下面

    2、Folder ---- 文件夹打开后的界面

    打开后的界面有标题和文件夹列表2部分组成。
    2.1、点击图标打开文件夹
    之前有介绍过在Launcher\src\com\android\launcher3\touch\ItemClickHandler.java中处理点击事件。

        private static void onClickFolderIcon(View v) {
            Folder folder = ((FolderIcon) v).getFolder();
            if (!folder.isOpen() && !folder.isDestroyed()) {
                // Open the requested folder
                folder.animateOpen();//打开文件夹
                StatsLogManager.newInstance(v.getContext()).logger().withItemInfo(folder.mInfo)
                        .log(LAUNCHER_FOLDER_OPEN);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.2、文件夹打开动画

    private void animateOpen(List<WorkspaceItemInfo> items, int pageNo, boolean skipUserEventLog) {
            Folder openFolder = getOpen(mLauncher);
            if (openFolder != null && openFolder != this) {
                // Close any open folder before opening a folder.
                openFolder.close(true);
            }
            mContent.bindItems(items);//文件夹内图标相关逻辑
            centerAboutIcon();//folder坐标和宽高计算
            mItemsInvalidated = true;
            updateTextViewFocus();
    
            mIsOpen = true;
            ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.3、mContent.bindItems
    Launcher\src\com\android\launcher3\folder\FolderPagedView.java中
    文件夹内图标排列:

    //循环列表,判断是否超过当前页面最大值,创建新页面等逻辑
    public void arrangeChildren(List<View> list) {
    ...
    setupContentDimensions(itemCount);//根据总数计算行列
    mOrganizer.getMaxItemsPerPage()//最大值判断
    createAndAddNewPage();//创建新页
    ...
    }
    	//创建新页面,也是celllayout
        private CellLayout createAndAddNewPage() {
            DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
            CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this);
            page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
            page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
            page.setInvertIfRtl(true);
            page.setGridSize(mGridCountX, mGridCountY);//设置行列号
    
            addView(page, -1, generateDefaultLayoutParams());
            return page;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Launcher\src\com\android\launcher3\folder\FolderGridOrganizer.java
    文件夹网格计算:

    //从它的构造方法可以看出,文件夹最大行列数来自于DeviceProfile配置
    // Launcher\res\xml\device_profiles_onyx.xml
    public FolderGridOrganizer(InvariantDeviceProfile profile) {
            mMaxCountX = profile.numFolderColumns;
            mMaxCountY = profile.numFolderRows;
            mMaxItemsPerPage = mMaxCountX * mMaxCountY;
    }
    //从注释可以理解一下这个行列数计算的规则。
    //1、Y《X
    //2、X = 总数的平方根向上取整(比如总数=8,X = 3,Y = 3;总数3,x=2 y=2)
    //3、X和Y小于文件夹最大行列数配置
        /**
         * Calculates the grid size such that {@param count} items can fit in the grid.
         * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
         * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
         */
        private void calculateGridSize(int count) {
            boolean done;
            int gridCountX = mCountX;
            int gridCountY = mCountY;
    
            if (count >= mMaxItemsPerPage) {
                gridCountX = mMaxCountX;
                gridCountY = mMaxCountY;
                done = true;
            } else {
                done = false;
            }
    
            while (!done) {
                int oldCountX = gridCountX;
                int oldCountY = gridCountY;
                if (gridCountX * gridCountY < count) {
                    // Current grid is too small, expand it
                    if ((gridCountX <= gridCountY || gridCountY == mMaxCountY)
                            && gridCountX < mMaxCountX) {
                        gridCountX++;
                    } else if (gridCountY < mMaxCountY) {
                        gridCountY++;
                    }
                    if (gridCountY == 0) gridCountY++;
                } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
                    gridCountY = Math.max(0, gridCountY - 1);
                } else if ((gridCountX - 1) * gridCountY >= count) {
                    gridCountX = Math.max(0, gridCountX - 1);
                }
                done = gridCountX == oldCountX && gridCountY == oldCountY;
            }
    
            mCountX = gridCountX;
            mCountY = gridCountY;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    3、FolderIcon文件夹图标
    在桌面上和应用图标一样的文件夹图标,点击即可打开文件夹。
    根据行列设计,计算缩放和小图标大小、排列,最终组合成FolderIcon效果。
    3.1、文件夹icon坐标计算
    Launcher\src\com\android\launcher3\folder\FolderIcon.java
    getLocalCenterForIndex方法:

        private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
            mTmpParams = mPreviewItemManager.computePreviewItemDrawingParams(
                    Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index), curNumItems, mTmpParams);
    
            mTmpParams.transX += mBackground.basePreviewOffsetX;
            mTmpParams.transY += mBackground.basePreviewOffsetY;
    
            float intrinsicIconSize = mPreviewItemManager.getIntrinsicIconSize();
            float offsetX = mTmpParams.transX + (mTmpParams.scale * intrinsicIconSize) / 2;
            float offsetY = mTmpParams.transY + (mTmpParams.scale * intrinsicIconSize) / 2;
    
            center[0] = Math.round(offsetX);
            center[1] = Math.round(offsetY);
            return mTmpParams.scale;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Launcher\src\com\android\launcher3\folder\PreviewItemManager.java 预览图管理类
    也就是FolderIcon小图标相关的逻辑计算类。
    computePreviewItemDrawingParams方法:

        PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
                PreviewItemDrawingParams params) {
            // We use an index of -1 to represent an icon on the workspace for the destroy and
            // create animations
            if (index == -1) {
                return getFinalIconParams(params);
            }
            return mIcon.mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Launcher\src\com\android\launcher3\folder\ClippedFolderIconLayoutRule.java

    public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
                PreviewItemDrawingParams params) {
    	...
    	if (index == EXIT_INDEX) {
                // 0 1 * <-- Exit position (row 0, col 2)
                // 2 3
                getGridPosition(0, 2, mTmpPoint);
            } else if (index == ENTER_INDEX) {
                // 0 1
                // 2 3 * <-- Enter position (row 1, col 2)
                getGridPosition(1, 2, mTmpPoint);
            } else if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
                // Items beyond those displayed in the preview are animated to the center
                mTmpPoint[0] = mTmpPoint[1] = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
            } else {
                getPosition(index, curNumItems, mTmpPoint);//走这里,根据图标个数和下标计算小图标位置
            }
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    因为FolderIcon场景比较多,初始化、图标拖拽等,所以入口和调用比较多
    如果要改原生效果,涉及的点稍微有点多。需要小心仔细修改后充分测试。

    尾注

    关于文件夹和文件夹icon预览的介绍就到这里。
    我是发现有一些bug,导致文件夹打开后图标丢失,跟踪到相关流程。
    顺手记录一下~

  • 相关阅读:
    婴幼儿蛋白质过敏怎么回事
    PHP 图像处理组件:Intervention/image
    OI回忆录
    嵌入式基础知识学习:Flash、EEPROM、RAM、ROM
    vulnhub靶场之ICA: 1
    49.【拷贝构造函数与重载】
    华为机试真题 Java 实现【全量和已占用字符集】
    【博客489】prometheus-----PromQL数据类型与Metrics数据类型
    vector的模拟实现
    路由重分布的概念与配置
  • 原文地址:https://blog.csdn.net/a396604593/article/details/126353907