很久没写Launcher分析的文章,最近实在太忙。今天七夕本来是想陪女朋友逛街 ,碰巧打台风呆在家里,就继续写一篇文章。今天主要是讲一下Launcher里面的Widget列表,这方面信息比较多,今天重点讲一下Widget信息收集和Launcher是如何显示Widget。这是这个系列第12篇文章,可是有关Launcher的分析感觉还有很多东西要写。
Widget列表是Android4.0以后才有的一种新特性,主要是可以直接查看Widget的缩略图,方便用户使用。而且Widget列表放到了AllApp里面,用一个TabHost管理。有关AllApp的TabHost切换,可以参考我前面的文章:AllApp全部应用列表(AppsCustomizeTabHost) 。Widget列表就是在AllApp列表后面。下面是Launcher4.0 的Widget列表:
(PS:新建的QQ群,有兴趣可以加入一起讨论:Android群:322599434)
1、AppsCustomizePagedView关系
首先看看Launcher是如何获取系统里面所有的Widget信息,这一部分都在AppsCustomizePagedView类里面,AppsCustomizePagedView类是同时管理显示Widget和所有应用列表。这两者都是这个类负责显示,看名字我们可以看到一个很熟悉的名字PagedView类,这个类前面我已经分析过,是一个十分重要的积累,继承于ViewGroup。主界面的WorkSpace也是继承于PagedView,PagedView主要是实现了页面滑动功能。AppsCustomizePagedView的基类同样是PagedView,下面看看他们的继承关系:
从上面的继承关系可以看到,AppsCustomizePagedView也是一个GroupView,主要就是用来显示其他view,只是多了页面滑动和拖曳功能。其实这个继承关系跟WorkSpace是基本一样,只是中间的类有点不一样。不了解的朋友,可以看我以前WorkSpace的分析文章。
2、Widgets信息收集
下面我们看看AppsCustomizePagedView里面如何统计系统里面Widget的信息。所有的Widget信息会最后保存到一个队列里面:
//Edited by mythou
//http://www.cnblogs.com/mythou/
private ArrayList<Object> mWidgets; //widget资源
下面我们看看,如何统计收集信息
//Edited by mythou
//http://www.cnblogs.com/mythou/
public void updatePackages() { //清空WIdget列表 boolean wasEmpty = mWidgets.isEmpty(); mWidgets.clear();
//这里获取的是Widget信息 List<AppWidgetProviderInfo> widgets = AppWidgetManager.getInstance( mLauncher).getInstalledProviders(); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
//获取所有的快捷方式,需要说明的是快捷方式同样显示在Widget里面 List<ResolveInfo> shortcuts = mPackageManager.queryIntentActivities( shortcutsIntent, 0);
//遍历所有的Widget插件 for (AppWidgetProviderInfo widget : widgets) {
//判断插件的最小高宽是否为0,为0不需要显示 if (widget.minWidth > 0 && widget.minHeight > 0) {
//添加到队列 mWidgets.add(widget); } else { Log.e(LOG_TAG, "Widget " + widget.provider + " has invalid dimensions (" + widget.minWidth + ", " + widget.minHeight + ")"); } }
//直接添加所有快捷方式 mWidgets.addAll(shortcuts); Collections.sort(mWidgets, new LauncherModel.WidgetAndShortcutNameComparator( mPackageManager)); updatePageCounts(); if (wasEmpty) { // The next layout pass will trigger data-ready if both widgets and // apps are set, so request // a layout to do this test and invalidate the page data when ready. if (testDataReady()) requestLayout(); } else { cancelAllTasks(); invalidatePageData(); } }
上面就是如何获取系统Widget信息的相关代码,主要是通过AppWidgetManager服务获取相关信息。需要注意的是,快捷方式也会显示在Widget列表里面,获取快捷方式的方法,主要是通过Intent的标记识别。通过PackageManager.queryIntentActivities,可以指定过滤所有含有ACTION_CREATE_SHORTCUT标识的程序,这些作为快捷方式也会被添加到Widget队列里面。
3、Widget信息初始化
上面说的是如何获取Widget的相关对象信息,我们可以看到ArrayList里面的类型也是初始化为Object。下面我们看看如何提取Widget里面的信息,然后初始化为View在界面上面显示。
//Edited by mythou
//http://www.cnblogs.com/mythou/
public void syncWidgetPageItems(final int page, final boolean immediate) { int numItemsPerPage = mWidgetCountX * mWidgetCountY; //计算Widget相关的长宽和边距,用于后面确定位置 final ArrayList<Object> items = new ArrayList<Object>(); int contentWidth = mWidgetSpacingLayout.getContentWidth(); final int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX); int contentHeight = mWidgetSpacingLayout.getContentHeight(); final int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY); // 这里是计算Widget的数目和每页数目,用于后面计算WIdget的预览图 int offset = page * numItemsPerPage; for (int i = offset; i < Math.min(offset + numItemsPerPage, mWidgets.size()); ++i) { items.add(mWidgets.get(i)); } //获取Widget信息,填充到PagedViewWidget对象里面 final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page + mNumAppsPages); layout.setColumnCount(layout.getCellCountX());
//遍历所有widget和快捷方式 for (int i = 0; i < items.size(); ++i) { Object rawInfo = items.get(i); PendingAddItemInfo createItemInfo = null; PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( R.layout.apps_customize_widget, layout, false);
//Widget信息 if (rawInfo instanceof AppWidgetProviderInfo) { // Fill in the widget information AppWidgetProviderInfo info = (AppWidgetProviderInfo) rawInfo; createItemInfo = new PendingAddWidgetInfo(info, null, null); int[] cellSpans = mLauncher.getSpanForWidget(info, null); widget.applyFromAppWidgetProviderInfo(info, -1, cellSpans, mHolographicOutlineHelper); widget.setTag(createItemInfo); }
//快捷方式
else if (rawInfo instanceof ResolveInfo) { // Fill in the shortcuts information ResolveInfo info = (ResolveInfo) rawInfo; createItemInfo = new PendingAddItemInfo(); createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; createItemInfo.componentName = new ComponentName( info.activityInfo.packageName, info.activityInfo.name); widget.applyFromResolveInfo(mPackageManager, info, mHolographicOutlineHelper); widget.setTag(createItemInfo); } widget.setOnClickListener(this); widget.setOnLongClickListener(this); widget.setOnTouchListener(this); widget.setOnKeyListener(this); //计算每个Widget的位置和布局信息 int ix = i % mWidgetCountX; int iy = i / mWidgetCountX; GridLayout.LayoutParams lp = new GridLayout.LayoutParams( GridLayout.spec(iy, GridLayout.LEFT), GridLayout.spec(ix, GridLayout.TOP)); lp.width = cellWidth; lp.height = cellHeight; lp.setGravity(Gravity.TOP | Gravity.LEFT); if (ix > 0) lp.leftMargin = mWidgetWidthGap; if (iy > 0) lp.topMargin = mWidgetHeightGap; layout.addView(widget, lp); }
上面是从我们获取的Widget对象里面提取Widget信息,包括位置,最小长宽和预览图。这里分开了Widget和快捷方式,这两种处理稍有不同,可以根据上面代码对比。下面以Widget为例,说说如何生成界面显示的效果。
4、Widget预览图
其实界面上我们看到的Widget预览图也是一个布局生成的,就是一个LinearLayout,下面我们简单看看布局文件R.layout.apps_customize_widget:
//Edited by mythou
//http://www.cnblogs.com/mythou/
<com.android.launcher2.PagedViewWidget android:background="@drawable/focusable_view_bg" android:focusable="true"> <LinearLayout android:orientation="horizontal"> <!--Widget名称和占用空间--> <TextView android:textSize="20sp" /> <TextView android:textSize="30sp" /> </LinearLayout> <!-- Widget的预览图--> <com.android.launcher2.PagedViewWidgetImageView android:id="@+id/widget_preview" android:scaleType="matrix" /> </com.android.launcher2.PagedViewWidget>
上面是Widget预览图的布局(我这里删除了很多属性,只是给个布局示意),主要就是用了两个TextView和一个ImageView实现,预览图的ImageView被重载了,实现了其他功能。根部局的LinearLayout也被重载,加入其他功能。这里不针对这两个类详细分析,PagedViewWidget里面实现了较多功能,包括主要的重绘功能。PagedViewWidgetImageView基本没有做什么事,可以直接当做ImageView使用。
上面的信息初始化过程,就是加载了这个布局,然后生成对应的对象,然后设置各种信息以及动作监听器。
//Edited by mythou
//http://www.cnblogs.com/mythou/
PagedViewWidget widget = (PagedViewWidget) mLayoutInflater.inflate( R.layout.apps_customize_widget, layout, false);
5、结语
从收集系统信息到初始化相关信息到生成对象大概就是这个过程。Widget缩略图生成过程比较复杂,这个下次再说吧,今天大伙早点休息,七夕快乐!
2013-8-13
Edited by 泡泡糖
系列文章:
Edited by mythou
原创博文,转载请标明出处:http://www.cnblogs.com/mythou/p/3256212.html