很久沒寫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 泡泡糖
系列文章:
Android Launcher分析和修改1——Launcher默認界面配置(default_workspace)
Android Launcher分析和修改2——Icon修改、界面布局調整、壁紙設置
Android Launcher分析和修改3——Launcher啟動和初始化
Android Launcher分析和修改4——初始化加載數據
Android Launcher分析和修改5——HotSeat分析
Android Launcher分析和修改6——頁面滑動(PagedView
Android Launcher分析和修改7——AllApp全部應用列表(AppsCustomizeTabHost)
Android Launcher分析和修改8——AllAPP界面拖拽元素(PagedViewWithDraggableItems)
Android Launcher分析和修改9——Launcher啟動APP流程
Android Launcher分析和修改10——HotSeat深入進階
Android Launcher分析和修改11——自定義分頁指示器(paged_view_indicator)
Edited by mythou
原創博文,轉載請標明出處:http://www.cnblogs.com/mythou/p/3256212.html