轉載請标明出處:http://blog.csdn.net/sk719887916/article/details/47027263
不少開發項目中都會有widget功能,别小瞧了它,他也是android的七大元件之一,對widget陌生的朋友可以閱讀下我的上篇文章< Android Widget工作原理詳解(一)>關于内部的介紹,還沒掌握的同學不要擔心,開發AppWidget套路很簡單,今天我們就實作一個可以加入listView滑動的widget,熟悉下一個普通widget的開發步驟。
一 建立AppWidgetProvider
此類是widget的控制核心,主要控制添加,删除,更新等。他是Broadcast的子類,可以擁有廣播的一切特性。 建立 MyAppListWidgetProvider類繼承AppWidgetProvider,實作其一下方法,onUpdate(),onReceive(), onEnabled(Context context) , onDeleted(), onDisabled()後面三方法可選而不可選。
1 onUpdate() 此方法一般處理widget的建立布局和更新UI操作,當widget添加到桌面會觸發onUpdate()方法,接下我們可以在此裡通過擷取remoteViews來給widget加載一個布局,遠端視圖前面也說過,它是widget的資源管理工具,我們可以用來給widget轉換一個它支援布局,僅支援特定的view,下面我為它加載一個listView,在widget上給某個控件設定點選事件采用PendingIntent,通過new一個延時意圖,然後 remoteViews.setOnClickPendingIntent( )來注冊點選事件。更新布局可以獲得用WidgetManager..updateAppWidget(thisWidget, remoteViews)來加載或更新widget布局,也可以通過onReceive() 收到一個自定義的廣播來調用此方法更新布局也可以。
[java] view plain copy
- @Override
[java] view plain copy
- public void onUpdate(Context context, AppWidgetManager appWidgetManager,
- int[] appWidgetIds) {
- // 擷取Widget的元件名
- ComponentName thisWidget = new ComponentName(context,
- MyAppListWidgetProvider.class);
- // 建立一個RemoteView
- RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
- R.layout.my_widget_layout);
- // 把這個Widget綁定到RemoteViewsService
- Intent intent = new Intent(context, MyRemoteViewsService.class);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[0]);
- // 設定擴充卡
- remoteViews.setRemoteAdapter(R.id.widget_list, intent);
- // 設定當顯示的widget_list為空顯示的View
- remoteViews.setEmptyView(R.id.widget_list, R.layout.none_data);
- // 點選清單觸發事件
- Intent clickIntent = new Intent(context, MyAppListWidgetProvider.class);
- // 設定Action,友善在onReceive中差別點選事件
- clickIntent.setAction(clickAction);
- clickIntent.setData(Uri.parse(clickIntent.toUri(Intent.URI_INTENT_SCHEME)));
- PendingIntent pendingIntentTemplate = PendingIntent.getBroadcast(
- context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- remoteViews.setPendingIntentTemplate(R.id.widget_list,
- pendingIntentTemplate);
- // 重新整理按鈕
- final Intent refreshIntent = new Intent(context,
- MyAppListWidgetProvider.class);
- refreshIntent.setAction("refresh");
- final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(
- context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- remoteViews.setOnClickPendingIntent(R.id.button_refresh,
- refreshPendingIntent);
- // 更新Wdiget
- appWidgetManager.updateAppWidget(thisWidget, remoteViews);
- }
2 onReceive()
此方功類似廣播的onReceive()用發,用開接收和處理廣播,如果我們在manifest.xml注冊了MyAppListWidgetProvider為一個appwidget,那麼不必須為此廣播加上widget标示,添加一action:<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />,下面的 <meta-data>标簽用來定義widget的屬性,指定一個widget描述資訊,具體釋義請閱讀 上篇widget原理詳解文章,
[html] view plain copy
- <!-- Widget必須添加到manifest檔案中,和Broadcaset Receiver一樣使用“receiver”标簽 -->
- <receiver android:name=".MyAppListWidgetProvider" >
- <!-- 此處設定Wdiget更新動作 -->
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
- </intent-filter>
- <!-- 此處設定Widget的描述資源res/xml/my_widget.xml -->
- <meta-data
- android:name="android.appwidget.provider"
- android:resource="@xml/widget_info"
- >
- </meta-data>
- </receiver>
onReceive裡處理代碼邏輯,比如我這裡用來接收widget的用來更新我們在onUpdate()給重新整理按鈕定義的點選事件,處理重新整理界面需求, [java] view plain copy
- @Override
- public void onReceive(Context context, Intent intent) {
- super.onReceive(context, intent);
- String action = intent.getAction();
- if (action.equals("refresh")) {
- // 重新整理Widget
- final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
- final ComponentName cn = new ComponentName(context,
- MyAppListWidgetProvider.class);
- MyRemoteViewsFactory.mList.add("音樂"+i);
- // 這句話會調用RemoteViewSerivce中RemoteViewsFactory的onDataSetChanged()方法。
- mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn),
- R.id.widget_list);
- } else if (action.equals(clickAction)) {
- // 單擊Wdiget中ListView的某一項會顯示一個Toast提示。
- Toast.makeText(context, intent.getStringExtra("content"),
- Toast.LENGTH_SHORT).show();
- }
- i=i+1;
- }
3 onEnabled(Context context)
當wdiget可用正在拖進桌面時觸發,這裡我們一般可以進行一些變量初始的工作 [java] view plain copy
- @Override
- public void onEnabled(Context context) {
- // TODO Auto-generated method stub
- super.onEnabled(context);
- Toast.makeText(context, "使用者将widget添加桌面了",
- Toast.LENGTH_SHORT).show();
- }
4 onDeleted(), widget被删除了,這裡我們通常用于釋放一些對象和視圖資源,便于防止記憶體洩露。
[java] view plain copy
- @Override
- public void onDeleted(Context context, int[] appWidgetIds) {
- // TODO Auto-generated method stub。
- Toast.makeText(context, "使用者将widget從桌面移除了",
- Toast.LENGTH_SHORT).show();
- super.onDeleted(context, appWidgetIds);
- }
5 onDisabled() widget在被拖動的時候觸發,這是widget是無法點選的,當停止拖動操作時widget可用。
二 建立RemoteViewsFactory
遠端視圖工廠,用來傳回Remoteviews,其通過adpter進行工作的,這裡我們調用 MyRemoteViewsFactory.mList.add()方法;給widget上的Listview加入資料。
[java] view plain copy
- public class MyRemoteViewsFactory implements RemoteViewsFactory {
- private final Context mContext;
- public static List<String> mList = new ArrayList<String>();
- public MyRemoteViewsFactory(Context context, Intent intent) {
- mContext = context;
- }
- @Override
- public void onCreate() {
- // 需要顯示的資料
- mList.add("");
- for (int i = 0; i < 5; i++) {
- mList.add("item"+ i);
- }
- }
- @Override
- public void onDataSetChanged() {
- }
- @Override
- public void onDestroy() {
- mList.clear();
- }
- @Override
- public int getCount() {
- return mList.size();
- }
- @Override
- public RemoteViews getViewAt(int position) {
- if (position < 0 || position >= mList.size())
- return null;
- String content = mList.get(position);
- // 建立在目前索引位置要顯示的View
- final RemoteViews rv = new RemoteViews(mContext.getPackageName(),
- R.layout.my_widget_layout_item);
- // 設定要顯示的内容
- rv.setTextViewText(R.id.widget_list_item_tv, content);
- // 填充Intent,填充在AppWdigetProvider中建立的PendingIntent
- Intent intent = new Intent();
- // 傳入點選行的資料
- intent.putExtra("content", content);
- rv.setOnClickFillInIntent(R.id.widget_list_item_tv, intent);
- return rv;
- }
- @Override
- public RemoteViews getLoadingView() {
- return null;
- }
- @Override
- public int getViewTypeCount() {
- return 1;
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public boolean hasStableIds() {
- return true;
- }
三 RemoteViewsService
RemoteViewsService子類提供了RemoteViewsFactory用于填充遠端集合視圖。
具體地說,其子類RemoteViewsService是一個遠端的服務擴充卡 可以請求RemoteViews,管理RemoteViews的服務。我們繼承RemoteViewsService來獲得一個視圖工廠,
[java] view plain copy
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public class MyRemoteViewsService extends RemoteViewsService {
- @Override
- public RemoteViewsFactory onGetViewFactory(Intent intent) {
- return new MyRemoteViewsFactory(this.getApplicationContext(), intent);
- }
- }
四 增加widet基礎屬性配置
1 添加widget描述檔案 我們為widget新增一個描述xml,在res/下建立一個xml檔案目錄,然後建立widget_info.xml檔案,具體如下 給widget指定了最小的寬高和浏覽的基礎視圖,包括其具體的布局檔案。具體介紹請看上篇文章 --- widget原理詳解。
[java] view plain copy
- <?xml version="1.0" encoding="utf-8"?>
- <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:initialLayout="@layout/my_widget_layout"
- android:minHeight="120dp"
- android:minWidth="280dp"
- android:previewImage="@drawable/ic_launcher"
- android:resizeMode="horizontal|vertical"
- android:updatePeriodMillis="0" >
- <!--
- sdk1.5之後updatePeriodMillis已失效,置為0,循環執行自行在代碼中實作。
- 至于其他屬性可以查一下。在其他随筆中我也給出了
- -->
- </appwidget-provider>
2 建立widget資源檔案xml 為widget建立一個實際要加載,也就是我直覺的看到的視圖布局。此布局通過widgetInfo的android:initialLayout="@layout/my_widget_layout"
屬性來指定。而widget描述資訊我們在 manifest.xml中 用<meta-data>标簽用來指定。
[html] view plain copy
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="200dp"
- android:background="@android:color/white"
- android:orientation="vertical" >
- <Button
- android:id="@+id/button_refresh"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:background="@drawable/lite_widget_item_choosed_background_icon"
- android:textColor="@android:color/white"
- android:layout_marginTop="2dp"
- android:text="重新整理" />
- <ListView
- android:id="@+id/widget_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:cacheColorHint="#00000000"
- android:scrollbars="none" />
- <!-- 此處的ListView 可以換成StackView或者GridView -->
- </LinearLayout>
通過以上的步驟我們簡單的實作了一個widget,用于初學者學習和交流,比較複雜的widget邏輯我們還會加入網絡通路功能,和一些和sevice進行資料互動,如果想要widget實作自動加入到桌面,或者widget支援自定義控件的話,第一可以将我的app變成系統app,第二,采用重寫 Remoteviews來支援我們自定義的view,具體實作邏輯後面再介紹,謝謝閱讀。