Launcher3小部件Widget添加流程分析 首先,看看Launcher3原生的Widget是如何获取到并显示的。原生的Widget是在AllApps界面显示的,也就是抽屉界面。 在La

时间:2021-10-21 09:19:57

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中去了。