Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android的Widget桌面應用學習

Android的Widget桌面應用學習

編輯:關於Android編程

一、Android應用的Widget的介紹

介紹:Android應用的Widget是應用程序窗口小部件(Widget)是微型的應用程序視圖,它可以被嵌入到其它應用程序中(比如桌面)並接收周期性的更新。你可以通過一個App Widget Provider來發布一個Widget。
圖片:首先上一張圖來給大家看一看效果。
這裡寫圖片描述

二、一些必要的概念介紹

2.1、AppWidgetProvider類

AppWidgetProvider 繼承自 BroadcastReceiver,它能接收 widget 相關的廣播,例如 widget 的更新、刪除、開啟和禁用等。

AppWidgetProvider中的廣播處理函數如下:
onUpdate()
當 widget 更新時被執行。同樣,當用戶首次添加 widget 時,onUpdate() 也會被調用,這樣 widget 就能進行必要的設置工作(如果需要的話) 。但是,如果定義了 widget 的 configure屬性(即android:config,後面會介紹),那麼當用戶首次添加 widget 時,onUpdate()不會被調用;之後更新 widget 時,onUpdate才會被調用。
onAppWidgetOptionsChanged()
當 widget 被初次添加 或者 當 widget 的大小被改變時,執行onAppWidgetOptionsChanged()。你可以在該函數中,根據 widget 的大小來顯示/隱藏某些內容。可以通過 getAppWidgetOptions() 來返回 Bundle 對象以讀取 widget 的大小信息,Bundle中包括以下信息:
OPTION_APPWIDGET_MIN_WIDTH – 包含 widget 當前寬度的下限,以dp為單位。
OPTION_APPWIDGET_MIN_HEIGHT – 包含 widget 當前高度的下限,以dp為單位。
OPTION_APPWIDGET_MAX_WIDTH – 包含 widget 當前寬度的上限,以dp為單位。
OPTION_APPWIDGET_MAX_HEIGHT – 包含 widget 當前高度的上限,以dp為單位。
onAppWidgetOptionsChanged() 是 Android 4.1 引入的。
onDeleted(Context, int[])
當 widget 被刪除時被觸發。
onEnabled(Context)
當第1個 widget 的實例被創建時觸發。也就是說,如果用戶對同一個 widget 增加了兩次(兩個實例),那麼onEnabled()只會在第一次增加widget時觸發。
onDisabled(Context)
當最後1個 widget 的實例被刪除時觸發。
onReceive(Context, Intent)
接收到任意廣播時觸發,並且會在上述的方法之前被調用。

總結,AppWidgetProvider 繼承於 BroadcastReceiver。實際上,App Widge中的onUpdate()、onEnabled()、onDisabled()等方法都是在 onReceive()中調用的,是onReceive()對特定事情的響應函數。

2.2、AppWidgetProviderInfo

AppWidgetProviderInfo描述一個App Widget元數據,比如App Widget的布局,更新頻率,以及AppWidgetProvider 類,這個文件是在res/xml中定義的,後綴為xml。下面是一個事例,還有AppWidgetProviderInfo中的一些常用的屬性介紹:

文件名:widget_weather.xml



    

示例說明:
minWidth 和minHeight
它們指定了App Widget布局需要的最小區域。 缺省的App Widgets所在窗口的桌面位置基於有確定高度和寬度的單元網格中。如果App Widget的最小長度或寬度和這些網格單元的尺寸不匹配,那麼這個App Widget將上捨入(上捨入即取比該值大的最接近的整數——譯者注)到最接近的單元尺寸。
注意:app widget的最小尺寸,不建議比 “4x4” 個單元格要大。關於app widget的尺寸,後面在詳細說明。
minResizeWidth 和 minResizeHeight
它們屬性指定了 widget 的最小絕對尺寸。也就是說,如果 widget 小於該尺寸,便會因為變得模糊、看不清或不可用。 使用這兩個屬性,可以允許用戶重新調整 widget 的大小,使 widget 的大小可以小於 minWidth 和 minHeight。
注意:
(01) 當 minResizeWidth 的值比 minWidth 大時,minResizeWidth 無效;當 resizeMode 的取值不包括 horizontal 時,minResizeWidth 無效。
(02) 當 minResizeHeight 的值比 minHeight 大時,minResizeHeight 無效;當 resizeMode 的取值不包括 vertical 時,minResizeHeight 無效。
updatePeriodMillis
它定義了 widget 的更新頻率。實際的更新時機不一定是精確的按照這個時間發生的。建議更新盡量不要太頻繁,最好是低於1小時一次。 或者可以在配置 Activity 裡面供用戶對更新頻率進行配置。 實際上,當updatePeriodMillis的值小於30分鐘時,系統會自動將更新頻率設為30分鐘!關於這部分,後面會詳細介紹。
注意: 當更新時機到達時,如果設備正在休眠,那麼設備將會被喚醒以執行更新。如果更新頻率不超過1小時一次,那麼對電池壽命應該不會造成多大的影響。 如果你需要比較頻繁的更新,或者你不希望在設備休眠的時候執行更新,那麼可以使用基於 alarm 的更新來替代 widget 自身的刷新機制。將 alarm 類型設置為 ELAPSED_REALTIME 或 RTC,將不會喚醒休眠的設備,同時請將 updatePeriodMillis 設為 0。
initialLayout
指向 widget 的布局資源文件
configure
可選屬性,定義了 widget 的配置 Activity。如果定義了該項,那麼當 widget 創建時,會自動啟動該 Activity。
previewImage
指定預覽圖,該預覽圖在用戶選擇 widget 時出現,如果沒有提供,則會顯示應用的圖標。該字段對應在 AndroidManifest.xml 中 receiver 的 android:previewImage 字段。由 Android 3.0 引入。
autoAdvanceViewId
指定一個子view ID,表明該子 view 會自動更新。在 Android 3.0 中引入。
resizeMode
指定了 widget 的調整尺寸的規則。可取的值有: “horizontal”, “vertical”, “none”。”horizontal”意味著widget可以水平拉伸,“vertical”意味著widget可以豎值拉伸,“none”意味著widget不能拉伸;默認值是”none”。Android3.1 引入。
widgetCategory
指定了 widget 能顯示的地方:能否顯示在 home Screen 或 lock screen 或 兩者都可以。它的取值包括:”home_screen” 和 “keyguard”。Android 4.2 引入。
initialKeyguardLayout
指向 widget 位於 lockscreen 中的布局資源文件。Android 4.2 引入。

其中比較重要的是:android:initialLayout=”@layout/widget_weather”這句話,這句話指向的是Widget的布局文件名。

三、Widget使用步驟

1、第一步,設計應用的布局。

就是一個普通的布局文件,不過長寬要設置合適。
如圖,我就給一張布局的圖片,布局代碼自行完成。
這裡寫圖片描述

注意:這裡介紹一些關於布局的知識,也是摘自別人的博客,一起學習一下。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxibG9ja3F1b3RlPg0KCTxwPjGhosztvNMgd2lkZ2V0ILW9bG9jayBzY3JlZW7W0DwvcD4NCgk8cD7ErMjPx+m/9s/CKLy0srvJ6NbDYW5kcm9pZDp3aWRnZXRDYXRlZ29yecr00NQpo6xBbmRyb2lkyse9q3dpZGdldMztvNO1vSBob21lIHNjcmVlbiDW0KGjPGJyIC8+DQoJtavU2kFuZHJvaWQgNC4y1tCjrMj008O7p8+jzfsgd2lkZ2V0IL/J0tSxu8ztvNO1vWxvY2sgc2NyZWVu1tCjrL/J0tTNqLn9yejWwyB3aWRnZXQgtcQ8YnIgLz4NCglhbmRyb2lkOndpZGdldENhdGVnb3J5IMr00NSw/Lqsa2V5Z3VhcmTAtM3qs8mhozwvcD4NCgk8cD61scTjsNEgd2lkZ2V0IMztvNO1vWxvY2sgc2NyZWVu1tDKsaOsxOO/ycTcttTL/L340NC2qNbGu6+y2df3o6zS1Mf4sfDT2sztvNO1vWhvbWUgc2NyZWVu1tC1xMfpv/ahoyDE48TcubvNqLn9PGJyIC8+DQoJZ2V0QXBwV2lkZ2V0T3B0aW9ucygpIMC0vfjQ0MXQts8gd2lkZ2V0IMrHsbvM7bzTtb1sb2NrIHNjcmVlbtbQo6y7ucrHaG9tZSBzY3JlZW7W0KGjzai5/TxiciAvPg0KCWdldEFwcGxpY2F0aW9uT3B0aW9ucygpILvxyKEgQnVuZGxlttTP86OsyLu687bByKEgQnVuZGxlPGJyIC8+DQoJtcRPUFRJT05fQVBQV0lER0VUX0hPU1RfQ0FURUdPUlnWtaO6yPTWtc6qIFdJREdFVF9DQVRFR09SWV9IT01FX1NDUkVFTqOsINTyse3KvrjDPGJyIC8+DQoJd2lkZ2V0ILG7zO2807W9aG9tZSBzY3JlZW7W0KO7IMj01rXOqiBXSURHRVRfQ0FURUdPUllfS0VZR1VBUkSjrNTyse3KvrjDIHdpZGdldDxiciAvPg0KCbG7zO2807W9bG9jayBzY3JlZW7W0KGjPC9wPg0KCTxwPsHtzeKjrMTj06a4w86qzO2807W9bG9jayBzY3JlZW7W0LXEIHdpZGdldCC1pbbAyrnTw9K7uPZsYXlvdXSjrL/J0tTNqLn9PGJyIC8+DQoJYW5kcm9pZDppbml0aWFsS2V5Z3VhcmRMYXlvdXQgwLTWuLaooaO2+CB3aWRnZXQgzO2807W9aG9tZSBzY3JlZW7W0LXEbGF5b3V01PK/ydLUzai5/TxiciAvPg0KCWFuZHJvaWQ6aW5pdGlhbExheW91dCDAtNa4tqihozwvcD4NCgk8cD4yoaKyvL7WPGJyIC8+DQoJPGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20160505/20160505090853688.png" title="\" />
如上圖所示,典型的App Widget有三個主要組件:一個邊界框(A bounding box),一個框架(a Frame),和控件的圖形控件(Widget Controls)和其他元素。App Widget並不支持全部的視圖窗口,它只是支持視圖窗口的一個子集,後面會詳細說明支持哪些視圖窗口。

要設計美觀的App Widget,建議在“邊界框和框架之間(Widget Margins)”以及“框架和控件(Widget
Padding)”之間填留有空隙。在Android4.0以上的版本,系統為自動的保留這些空隙。

3、Widget窗口大小

在AppWidgetProviderInfo中已經介紹了,minWidth 和minHeight 用來指定了App
Widget布局需要的最小區域。缺省的App Widgets所在窗口的桌面位置基於有確定高度和寬度的單元網格中。如果App
Widget的最小長度或寬度和這些網格單元的尺寸不匹配,那麼這個App
Widget將上捨入(上捨入即取比該值大的最接近的整數——譯者注)到最接近的單元尺寸。

例如,很多手機提供4x4網格,平板電腦能提供8x7網格。當widget被添加到時,在滿足minWidth和minHeight約束的前提下,它將被占領的最小數目的細胞。
粗略計算minWidth和minHeight,可以參考下面表格:
這裡寫圖片描述
這裡寫圖片描述

App Widget支持的布局和控件

Widget並不支持所有的布局和控件,而僅僅只是支持Android布局和控件的一個子集。
(01) App Widget支持的布局:
  FrameLayout
  LinearLayout
  RelativeLayout
  GridLayout
(02) App Widget支持的控件:
  AnalogClock
  Button
  Chronometer
  ImageButton
  ImageView
  ProgressBar
  TextView
  ViewFlipper
  ListView
  GridView
  StackView
  AdapterViewFlipper
  

2、第二步,編輯AppWidgetProviderInfo對應的資源文件。

文件名:weather_widget_info.xml



    

說明:
(01) android:previewImage,用於指定預覽圖片。即搜索到widget時,查看到的圖片。若沒有設置的話,系統為指定一張默認圖片。
(02) android:updatePeriodMillis 更新widget的時間間隔(ms)。在實際測試中,發現設置android:updatePeriodMillis的值為5秒時,不管用!跟蹤android源代碼,發現:
當android:updatePeriodMillis的值小於30分鐘時,會被設置為30分鐘。也就意味著,即使將它的值設為5秒,實際上也是30分鐘之後才更新。因此,我們若向動態的更新widget的某組件,最好通過service、AlarmManager、Timer等方式;本文采用的是service。

3、第三步,編寫類文件繼承AppWidgetProvider類,重寫方法

文件名:AppWidgetProvider.java
public class AppWidgetProvider extends android.appwidget.AppWidgetProvider {
    Gson gson;
    RecentWeatherBean recentWeatherBean;
    RetDataBean retDataBean;
    TodayBean todayBean;
    GetDate getDate;
    Context context;
    //保存地址和ID
    SharedPreferences sharedPreferences;
    //啟動AppWidgetService服務對應的action
    private final Intent SERVICE_INTENT = new Intent("android.appwidget.action.APP_WIDGET_SERVICE");
    //更新widget的廣播對應的action
    private final String ACTION_UPDATE_ALL = "com.llay.widget.UPDATE_ALL";

    //onUpdate()在更新widget時,被執行(當用戶點擊wiget界面時候可以調用這個方法)
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int i = 0; i < appWidgetIds.length; i++) {
            //創建一個Intent對象,跳轉到app界面
            Intent intent = new Intent(context, GetLocationActivity.class);
            //創建一個PendingIntent,包主intent
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            //獲取wiget布局
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_weather);
            remoteViews.setOnClickPendingIntent(R.id.widget_line, pendingIntent);
            appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

    //當widget被初次添加或者當widget的大小被改變時,被調用
    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        super.onDeleted(context, appWidgetIds);
    }

    //第一個widget被創建時調用
    @Override
    public void onEnabled(Context context) {
        //在第一個widget被創建時,開啟服務
        SERVICE_INTENT.setPackage("com.llay.admin.weather");
        context.startService(SERVICE_INTENT);
        super.onEnabled(context);
    }

    //最後一個widget被刪除時調用
    @Override
    public void onDisabled(Context context) {
        //在最後一個widget被刪除時,終止服務
        SERVICE_INTENT.setPackage("com.llay.admin.weather");
        context.stopService(SERVICE_INTENT);
        super.onDisabled(context);
    }


    //接收廣播的回調函數
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        this.context = context;
        if (ACTION_UPDATE_ALL.equals(action)) {
            getDate = new GetDate();
            getDate.execute("http://apis.baidu.com/apistore/weatherservice/recentweathers");
        } else {
            super.onReceive(context, intent);
        }

    }

    //請求數據
    public class GetDate extends AsyncTask {

        @Override
        protected String doInBackground(String... params) {
            sharedPreferences = context.getSharedPreferences("CityAndCode", Context.MODE_PRIVATE);
            String recentJson = Utils.request(params[0], "cityname=" + sharedPreferences.getString("locationCityName", "南通") + "&cityid=" + sharedPreferences.getInt("locationCityCode", 101190501));
            return recentJson;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            if (s != null) {
                gson = new Gson();
                recentWeatherBean = new RecentWeatherBean();
                recentWeatherBean = gson.fromJson(s, RecentWeatherBean.class);
                retDataBean = new RetDataBean();
                retDataBean = recentWeatherBean.getRetData();
                todayBean = new TodayBean();
                todayBean = retDataBean.getToday();
                //更新界面
                RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_weather);
                switch (todayBean.getType()) {
                    case "晴":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.sunny);
                        break;
                    case "多雲":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.cloudy);
                        break;
                    case "陰":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.overcast);
                        break;
                    case "陣雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.shower);
                        break;
                    case "雷陣雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.thundershower);
                        break;
                    case "小雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.light_rain);
                        break;
                    case "中雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.moderate_rain);
                        break;
                    case "大雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.heavy_rain);
                        break;
                    case "暴雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.storm);
                        break;
                    case "大暴雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.heavy_storm);
                        break;
                    case "小到中雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.light_to_moderate_rain);
                        break;
                    case "中到大雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.moderate_to_heavy_rain);
                        break;
                    case "大到暴雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.heavy_to_storm);
                        break;
                    case "暴雨到大暴雨":
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.storm_to_heavy_storm);
                        break;
                    default:
                        remoteViews.setImageViewResource(R.id.wigdet_image, R.mipmap.undefined);
                        break;
                }
                remoteViews.setTextViewText(R.id.widget_type, todayBean.getType());
                remoteViews.setTextViewText(R.id.widget_curtemp, todayBean.getCurTemp());
                remoteViews.setTextViewText(R.id.widget_date, todayBean.getDate());
                remoteViews.setTextViewText(R.id.widget_location, retDataBean.getCity());
                remoteViews.setTextViewText(R.id.widget_week, todayBean.getWeek());
                remoteViews.setTextViewText(R.id.widget_lowtemp, todayBean.getLowtemp());
                remoteViews.setTextViewText(R.id.widget_hightemp, todayBean.getHightemp());
                remoteViews.setTextViewText(R.id.widget_fengxiang, todayBean.getFengxiang());
                remoteViews.setTextViewText(R.id.widget_fengli, todayBean.getFengli());
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                ComponentName componentName = new ComponentName(context, AppWidgetProvider.class);
                appWidgetManager.updateAppWidget(componentName, remoteViews);
            }
        }
    }

}

說明:
(01)當我們創建第一個widget到桌面時,會執行onEnabled()。在onEnabled()中通過 context.startService(SERVICE_INTENT) 啟動服務AppWidgetService。服務的作用就是每隔5秒發送一個ACTION_UPDATE_ALL廣播給我們,用於更新widget中的數據。僅僅當我們創建第一個widget時才會啟動服務,因為onEnabled()只會在第一個widget被創建時才執行。
(02)當我們刪除最後一個widget到桌面時,會執行onDisabled()。在onDisabled()中通過 context.stopService(SERVICE_INTENT) 終止服務AppWidgetService。僅僅當我們刪除最後一個widget時才會終止服務,因為onDisabled()只會在最後一個widget被刪除時才執行。
(03)本工程中,每添加一個widget都會執行onUpdate()。例外情況:在定義android:configure的前提下,第一次添加widget時不會執行onUpdate(),而是執行android:configure中定義的activity。在onUpdate()方法中,有兩個類要特別注意:PendingIntent和RemoteViews。PendingIntent用戶包裹住一個Intent,然後再調用remoteViews.setOnClickPendingIntent(R.id.widget_line, pendingIntent);方法時,運行Intent。RemoteViews用戶獲取Widget中的控件,點擊Widget中的控件,調用remoteViews.setOnClickPendingIntent(R.id.widget_line, pendingIntent);方法,可以實現一些功能,如點擊Widget然後打開應用。
(04)onReceive()中,更新桌面的widget 以及響應按鈕點擊廣播。當收到ACTION_UPDATE_ALL廣播時,調用getDate.execute()方法來請求接口,然後再onPostExecute()中更新的widget的數據。

4、第四步,在AndroidManifest.xml中注冊AppWidgetProvider

因為AppWidgetProvider原本就是一個BroadCastReceiver,需要在AndroidManifest.xml中注冊。


        
            
                
                
            
            
        

5、第五步,其實這裡已經結束了,由於該項目是在後台使用Service實施每10S更新一次,所以還要添加Service類

文件名:AppWidgetService.java
public class AppWidgetService extends Service {
    //更新widget的廣播對應的action
    private final String ACTION_UPDATE_ALL = "com.llay.widget.UPDATE_ALL";
    //周期性更新widget的周期
    private static final int UPDATE_TIME = 10000;
    //周期性更新widget的線程
    private UpdateThread mUpdateThread;
    private Context mContext;
    //更新周期的計數
    private int count = 0;

    @Override
    public void onCreate() {
        //創建並開啟線程UpdateThread
        mUpdateThread = new UpdateThread();
        mUpdateThread.start();
        mContext = this.getApplicationContext();
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        //中斷線程,即結束線程。
        if (mUpdateThread != null) {
            mUpdateThread.interrupt();
        }
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    //服務開始時,即調用startService()時,onStartCommand()被執行。
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    //線程中發送廣播
    private class UpdateThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                count = 0;
                //一直在後台中發送廣播,action為"com.llay.widget.UPDATE_ALL"
                while (true) {
                    count++;
                    Intent updateIntent = new Intent(ACTION_UPDATE_ALL);
                    mContext.sendBroadcast(updateIntent);
                    Thread.sleep(UPDATE_TIME);
                }
            } catch (InterruptedException e) {
                //將InterruptedException定義在while循環之外,意味著拋出InterruptedException異常時,終止線程。
                e.printStackTrace();
            }
        }
    }
}

在AndroidManifest.xml中注冊Service


        
            
        
    

說明:
(01) onCreate() 在創建服務時被執行。它的作用是創建並啟動線程UpdateThread()。
(02) onDestroy() 在銷毀服務時被執行。它的作用是注銷線程UpdateThread()。
(03) 服務UpdateThread 每隔10秒,發送1個廣播ACTION_UPDATE_ALL。廣播ACTION_UPDATE_ALL在AppWidgetProvider被處理,用來更新widget中的數據。

總結:主要的核心代碼

1、當創建一個Widget時,調用onUpdate()方法和onEnabled()方法。
onUpdage()方法,用於用戶點擊桌面上的Widget,可以開發應用。核心代碼如下

for (int i = 0; i < appWidgetIds.length; i++) {
            //創建一個Intent對象,跳轉到app界面
            Intent intent = new Intent(context, GetLocationActivity.class);
            //創建一個PendingIntent,包裹住intent
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            //獲取wiget布局
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_weather);
            remoteViews.setOnClickPendingIntent(R.id.widget_line, pendingIntent);
            appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);

onEnabled()方法,用於新建第一個Widget時,發送Service對應的action,運行AppWidgetService類。核心代碼如下

//第一個widget被創建時調用
    @Override
    public void onEnabled(Context context) {
        //在第一個widget被創建時,開啟服務
        SERVICE_INTENT.setPackage("com.llay.admin.weather");
        context.startService(SERVICE_INTENT);
        super.onEnabled(context);
    }

2、運行Service,每10S發送BroadCast對應的action,核心代碼如下

//線程中發送廣播
    private class UpdateThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                count = 0;
                //一直在後台中發送廣播,action為"com.llay.widget.UPDATE_ALL"
                while (true) {
                    count++;
                    Intent updateIntent = new Intent(ACTION_UPDATE_ALL);
                    mContext.sendBroadcast(updateIntent);
                    Thread.sleep(UPDATE_TIME);
                }
            } catch (InterruptedException e) {
                //將InterruptedException定義在while循環之外,意味著拋出InterruptedException異常時,終止線程。
                e.printStackTrace();
            }
        }
    }

3、在AppWidgetProvider中的onReceive()方法中接收,BroadCast發送的action,執行相應的更新數據的方法。核心代碼如下

//接收廣播的回調函數
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        this.context = context;
        if (ACTION_UPDATE_ALL.equals(action)) {
            getDate = new GetDate();
            getDate.execute("http://apis.baidu.com/apistore/weatherservice/recentweathers");
        } else {
            super.onReceive(context, intent);
        }

    }

總結:思路

創建第一個Widget,發送action為”android.appwidget.action.APP_WIDGET_SERVICE”->
AndroidManifest.xml中收到此action,運行AppWidgetService.java文件->
AppWidgetService.java中每10S發送一個廣播action為”com.llay.widget.UPDATE_ALL”->
在AppWidgetProvider.java中的onReceive()方法,接收這個廣播action,執行更新->
刪除最後一個Widget,停止Service

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved