现在市面上大多数手机的桌面文件夹都是类似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的逻辑,还是挺简单的。