Launcher3小部件Widget添加流程分析
首先,看看Launcher3原生的Widget是如何获取到并显示的。原生的Widget是在AllApps界面显示的,也就是抽屉界面。
在Launcher.java 的onCreate()函数中先获取AppWidgetProviderInfo信息:
protected void onCreate(Bundle savedInstanceState) {
// Update customization drawer _after_ restoring the states
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.onPackagesUpdated(
LauncherModel.getSortedWidgetsAndShortcuts(this));
}
}
mAppsCustomizeContent 即显示AllApps和Widget的界面AppsCustomizePagedView,
LauncherModel.getSortedWidgetsAndShortcuts(this)从数据库中获取到经过排序的Widgets信息并传入AppsCustomizePagedView显示,具体实现就参考LauncherModel相关代码。
这里跳转到AppsCustomizePagedView的onPackagesUpdated函数中,在onPackagesUpdated中,会将前面获取到的信息赋值给一个全局的ArrayList<object> mWidgets,后续的所有操作都是从这个mWidgets中获取相关信息的。
在获取到mWidgets之后,就要将之添加显示到AppsCustomizePagedView中,Launcher3是在AppsCustomizePagedView的
@Override
public void syncPageItems(int page, boolean immediate) {
if (mContentType == ContentType.Widgets) {
syncWidgetPageItems(page, immediate);
} else {
syncAppsPageItems(page, immediate);
}
}
调用syncWidgetPageItems(page, immediate);按照每一页进行添加Widget到预览界面。
syncWidgetPageItems的主要功能就是将mWidgets添加到预览界面中,syncWidgetPageItems中会计算各个Widget的位置信息,并添加tag,具体实现参考源码。
Widget预览显示完后,怎么添加到主屏幕,也就是Workspace中呢?
首先是Launcher3原生的流程,即在AppsCustomizePagedView直接拖拽到Workspace中。
拖拽是onLongClick事件,拖拽是从beginDragging开始的:
@Override
protected boolean beginDragging(final View v) {
if (!super.beginDragging(v)) return false;
if (v instanceof PagedViewIcon) {
beginDraggingApplication(v);
} else if (v instanceof PagedViewWidget) {
if (!beginDraggingWidget(v)) {
return false;
}
}
// We delay entering spring-loaded mode slightly to make sure the UI
// thready is free of any work.
postDelayed(new Runnable() {
@Override
public void run() {
// We don't enter spring-loaded mode if the drag has been cancelled
if (mLauncher.getDragController().isDragging()) {
// Reset the alpha on the dragged icon before we drag
resetDrawableState();
// Go into spring loaded mode (must happen before we startDrag())
mLauncher.enterSpringLoadedDragMode();
}
}
}, 150);
return true;
}
这里的else if (v instanceof PagedViewWidget) {
if (!beginDraggingWidget(v)) {
return false;
}
}
进入beginDraggingWidget(v),就正式开始了我们的拖拽流程。
private boolean beginDraggingWidget(View v) {
·······
if (createItemInfo instanceof PendingAddWidgetInfo) {
//这里主要是计算widget的大小,以及缩放比例
}
········
boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
(((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
// 这里是绘制拖拽时在Workspace上显示的边框的 outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
false);
// 这里开始真正的拖拽
mLauncher.lockScreenOrientation();
mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
mDragController.startDrag(image, preview, this, createItemInfo,
DragController.DRAG_ACTION_COPY, previewPadding, scale);
outline.recycle();
preview.recycle();
return true;
}
到此,长按的拖拽事件就走到了Workspace和DragController中去了,在Workspace中
public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
final Canvas canvas = new Canvas();
int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
// The outline is used to visualize where the item will land if dropped
mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
size[1], clipAlpha);
}
绘制mDragOutline,就是我们拖拽时在Workspace下方显示的那个边框
拖拽事件,从mDragController.startDrag中处理,最终走到Workspace的onDragOver和onDropExternal中。
这里不赘述onDragOver的自动排序了,手放开后,Widget被添加到Workspace中是在onDropExternal中实现的。
private void onDropExternal(final int[] touchXY, final Object dragInfo,
final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
······
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
int span[] = new int[2];
span[0] = item.spanX;
span[1] = item.spanY;
mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
container, screenId, mTargetCell, span, null);
break;
······
可以看到Workspace中通过mLauncher.addAppWidgetFromDrop又返回给了Launcher处理。
addAppWidgetFromDrop主要做四件事,
一,获取hostView,也就是最终显示的那个view
AppWidgetHostView hostView = info.boundWidget;
二,获取Widget在provider里的id
if (hostView != null) {
appWidgetId = hostView.getAppWidgetId();
addAppWidgetImpl(appWidgetId, info, hostView, info.info);
} else {
// In this case, we either need to start an activity to get permission to bind
// the widget, or we need to start an activity to configure the widget, or both.
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
}
三,绑定Widget服务
if (options != null) {
success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
info.componentName, options);
} else {
success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
info.componentName);
}
最后就是添加View到Workspace中去了
if (success) {
addAppWidgetImpl(appWidgetId, info, null, info.info);
} else {
mPendingAddWidgetInfo = info.info;
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
info.componentName);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
}
正常情况下success一般都能返回true,需要设置的Widget会返回false,走 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);启动一个Activity,设置完成后再返回Launcher进行绑定流程。无论success是true 还是false,最终还是走到addAppWidgetImpl,在addAppWidgetImpl中又会调用completeAddAppWidget来完成添加和修改数据库,LauncherModel.addItemToDatabase(this, launcherInfo,container, screenId, cellXY[0], cellXY[1], false);
好啦,到这里,我们的Widget就成功添加到Workspace中去了。