桌面文件夹功能简介
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);
}
}
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;
...
}
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;
}
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;
}
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;
}
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);
}
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);//走这里,根据图标个数和下标计算小图标位置
}
...
}
因为FolderIcon场景比较多,初始化、图标拖拽等,所以入口和调用比较多
如果要改原生效果,涉及的点稍微有点多。需要小心仔细修改后充分测试。
尾注
关于文件夹和文件夹icon预览的介绍就到这里。
我是发现有一些bug,导致文件夹打开后图标丢失,跟踪到相关流程。
顺手记录一下~