探秘widget之launcher添加widget的流程分析

By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

最近打算研究下android的widget相关问题,并把一些心得在此稍作记录,哈哈,等研究完成了,如果有必要的话,也会把改动的源码贴出来,以飨读者。今天先来看看launcher2中添加widget的流程。
添加widget首先需要在laucher的空白处长按,所以首先定位在laucher的 public boolean onLongClick(View v) 中,看到:
if (mWorkspace.allowLongPress()) { if (cellInfo.cell == null) { if (cellInfo.valid) { // User long pressed on empty space mWorkspace.setAllowLongPress(false); mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); showAddDialog(cellInfo); } } else { if (!(cellInfo.cell instanceof Folder)) { // User long pressed on an item mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); mWorkspace.startDrag(cellInfo); } } }
可以看到跳转到了showAddDialog(cellInfo),寻找到:
private void showAddDialog(CellLayout.CellInfo cellInfo) { mAddItemCellInfo = cellInfo; mWaitingForResult = true; showDialog(DIALOG_CREATE_SHORTCUT); }
可以看到他携带着DIALOG_CREATE_SHORTCUT参数创建了一个Dialog,携带参数跳入Launcher.java的父类Activity.java的showDialog()方法,最终到达Launcher.java的onCreateDialog(int id)方法,代码如下:
@Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CREATE_SHORTCUT: return new CreateShortcut().createDialog(); case DIALOG_RENAME_FOLDER: return new RenameFolder().createDialog(); } return super.onCreateDialog(id); }
跳转到了CreateShortcut()的createDialog()方法:
Dialog createDialog() { mWaitingForResult = true; mAdapter = new AddAdapter(Launcher.this); final AlertDialog.Builder builder = new AlertDialog.Builder(Launcher.this); builder.setTitle(getString(R.string.menu_item_add_item)); builder.setAdapter(mAdapter, this); builder.setInverseBackgroundForced(true); AlertDialog dialog = builder.create(); dialog.setOnCancelListener(this); dialog.setOnDismissListener(this); dialog.setOnShowListener(this); return dialog; }
这里可以看到一个AddAdapter类,跳转去看看,这个就是定义长按后出现的对话框的内容:
public static final int ITEM_SHORTCUT = 0; public static final int ITEM_APPWIDGET = 1; public static final int ITEM_LIVE_FOLDER = 2; public static final int ITEM_WALLPAPER = 3;
如果我们需要在原来的对话框中添加新的内容,那么首先需要修改的就是这里,我们回到之前的地方接着往下走,dialog响应的点击事件,public void onClick(DialogInterface dialog, int which) :
case AddAdapter.ITEM_APPWIDGET: { int appWidgetId = Launcher.this.mAppWidgetHost.allocateAppWidgetId(); //这里生成了一个appWidgetId,供后面绑定AppWidgetProvider使用 Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK); //新建一个intent,该intent是打开一个现实Widgets列表的activity,该activity对应类AppWidgetPickActivity pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); // start the pick activity //设置EXTRA_APPWIDGET_ID startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET); break; }

这里看到点击widget条目之后,跳转打开一个新的pickIntent,其实际运行的为packages/apps/Settings/src/com/android/settings/AppWidgetPickActivity.java:
先在onCreate方法中创建了一个InstalledAppWidgets列表,该列表就是我们在界面上能见到的所有widgets。
在点击一个widgets,进入AppWidgetPickActivity.onClick事件监听,注意阅读该方法代码,它会进入else:
if (intent.getExtras() != null) { // If there are any extras, it's because this entry is custom. // Don't try to bind it, just pass it back to the app. setResultData(RESULT_OK, intent); } else { try { mAppWidgetManager.bindAppWidgetId(mAppWidgetId, intent.getComponent()); //绑定选中的桌面组件与mAppWidgetId result = RESULT_OK; //设置返回结果为ok
activity执行结束后面都会进入launcher.onActivityResult,查看该函数方法有两个关键的case:
case REQUEST_PICK_APPWIDGET: addAppWidget(data); break; case REQUEST_CREATE_APPWIDGET: completeAddAppWidget(data, mAddItemCellInfo);

接着跳转到launcher的addAppWidget(Intent data)里data为传递来的appWidgetId:
void addAppWidget(Intent data) { // TODO: catch bad widget exception when sent int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId); //先判断是否有配置页 if (appWidget.configure != null) { // Launch over to configure widget, if needed Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE); intent.setComponent(appWidget.configure); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise just add it onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); } }


通过onActivityResult(REQUEST_CREATE_APPWIDGET, Activity.RESULT_OK, data); 跳转回launcher.onActivityResult的

case REQUEST_CREATE_APPWIDGET:
completeAddAppWidget(data, mAddItemCellInfo);


@param data The intent describing the appWidgetId. * @param cellInfo The position on screen where to create the widget. */ private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo)
在 completeAddAppWidget(data, mAddItemCellInfo)中完成widget的添加。

private void completeAddAppWidget(Intent data, CellLayout.CellInfo cellInfo) { Bundle extras = data.getExtras(); int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); if (LOGD) Log.d(TAG, "dumping extras content=" + extras.toString()); //由appWidgetId获取widget的信息,例如大小等 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); // Calculate the grid spans needed to fit this widget CellLayout layout = (CellLayout) mWorkspace.getChildAt(cellInfo.screen); int[] spans = layout.rectToCell(appWidgetInfo.minWidth, appWidgetInfo.minHeight); // Try finding open space on Launcher screen final int[] xy = mCellCoordinates; if (!findSlot(cellInfo, xy, spans[0], spans[1])) { if (appWidgetId != -1) mAppWidgetHost.deleteAppWidgetId(appWidgetId); return; } // Build Launcher-specific widget info and save to database LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId); launcherInfo.spanX = spans[0]; launcherInfo.spanY = spans[1]; LauncherModel.addItemToDatabase(this, launcherInfo, LauncherSettings.Favorites.CONTAINER_DESKTOP, mWorkspace.getCurrentScreen(), xy[0], xy[1], false); if (!mRestoring) { mDesktopItems.add(launcherInfo); // Perform actual inflation because we're live launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo); launcherInfo.hostView.setTag(launcherInfo); mWorkspace.addInCurrentScreen(launcherInfo.hostView, xy[0], xy[1], launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked()); }


【探秘widget之launcher添加widget的流程分析】

    推荐阅读