Android 的widget的一些開發相關介紹

Widget是一種小巧但是功能強大的程序,使用戶能夠方便快捷的獲取信息,在PC上被廣泛的使用,現在隨着OPhone的推出,widget也進入到了手機領域,爲用戶帶來了方便的同時也爲開發者實現更多很酷想法的可能。在OPhone中有兩種widget開發方式,一種是以HTML+CSS+JavaScript的開發方式,另一種是沿用Android平臺的開發方式,本文介紹的是後面一種,在OPhone平臺上開發App widget。

widget一般開發方式介紹

下面以編寫一個時鐘的小程序來介紹如何編寫widget。

(1)創建一個類,讓其繼承類AppWidgetProvider,在AppWidgetProvider類中有許多的方法,例如onDelete(Context, int[]),onEnable(Context)等等,一般情況下我們紙需要重寫onUpdate(Context, AppWidgetManager, int[])這個方法就可以了,這個方法是當觸發器更新widget時候執行的操作。

(2)在項目的AndroidMenifest.xml文件中添加一個receiver標籤,讓其指向前面創建的AppWidgetProvider子類,內容如下:

  1. <receiver android:name="widget"  
  2.     android:label="@string/app_name"  
  3.     android:icon="@drawable/icon">  
  4.       <intent-filter>  
  5.          <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />    
  6.      </intent-filter>  
  7.          <meta-data android:name="android.appwidget.provider"    
  8.                android:resource="@xml/widget_setting" />    
  9.     </receiver>  
<receiver android:name="widget" android:label="@string/app_name" android:icon="@drawable/icon"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_setting" /> </receiver>

intent-filter中過濾了APPWIDGET_UPDATE事件,這個事件是由系統觸發的更新事件,每個widget必須包含這個事件;meta-data標籤描述的是widget的配置文件指向,該文件描述了widget的一些基本信息。

(3)編寫widget的provider文件信息,本例中該文件名叫做widget_setting.xml,開發者可以隨便取名,只要在AndroidMenifest.xml中寫正確就行。

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <appwidget-provider  
  3.  xmlns:android="http://schemas.android.com/apk/res/android"    
  4.  android:minWidth="100dp"    
  5.  android:minHeight="100dp"  
  6.  android:initialLayout="@layout/main"    
  7.  android:updatePeriodMillis="1000" >  
  8. </appwidget-provider>  
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="100dp" android:minHeight="100dp" android:initialLayout="@layout/main" android:updatePeriodMillis="1000" > </appwidget-provider>

minWidth和minHeight是widget的最小寬度和高度,這個值是一個參考值,系統會根據實際情況進行改變,initialLayout屬性指明瞭widget的視圖佈局文件,updatePeriodMillis屬性是widget每隔多久更新一次的時間,單位爲毫秒。

(4)接下來就是界面佈局,在這個示例中只需要一個TextView控件就可以,代碼如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <TextView    
  8.     android:layout_width="fill_parent"    
  9.     android:layout_height="wrap_content"    
  10.     android:text="@string/hello" android:id="@+id/text"/>  
  11. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" android:id="@+id/text"/> </LinearLayout>

準備工作完畢,接下來完善繼承自AppWidgetProvider的自定義類,重寫onUpdate(Context, AppWidgetManager,int[])函數,代碼如下:

  1. package com.dt.time;   
  2.     
  3. import java.util.Date;   
  4.     
  5. import android.appwidget.AppWidgetManager;   
  6. import android.appwidget.AppWidgetProvider;   
  7. import android.content.Context;   
  8. import android.widget.RemoteViews;   
  9.     
  10. public class widget extends AppWidgetProvider {   
  11.     
  12.    @Override  
  13.    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {   
  14.       // TODO Auto-generated method stub   
  15.       super.onUpdate(context, appWidgetManager, appWidgetIds);   
  16.          
  17.       //1. 獲取當前時間   
  18.       Date now = new Date();   
  19.       String strNow = now.toLocaleString();   
  20.       //2. 獲取RemoteViews對象   
  21.       RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);   
  22.       //3. 顯示時間到widget   
  23.       views.setTextViewText(R.id.text, strNow);   
  24.       //4. 更新widget   
  25.       appWidgetManager.updateAppWidget(appWidgetIds, views);   
  26.    }   
  27. }  
package com.dt.time; import java.util.Date; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.widget.RemoteViews; public class widget extends AppWidgetProvider { @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // TODO Auto-generated method stub super.onUpdate(context, appWidgetManager, appWidgetIds); //1. 獲取當前時間 Date now = new Date(); String strNow = now.toLocaleString(); //2. 獲取RemoteViews對象 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main); //3. 顯示時間到widget views.setTextViewText(R.id.text, strNow); //4. 更新widget appWidgetManager.updateAppWidget(appWidgetIds, views); } }

之後運行寫好的widget查看下成果,widget的啓動與普通程序不同,它不會在程序列表中顯示,而是要長按桌面在彈出的列表中選擇Widgets項目,之後選擇本示例time,下圖就是widget運行時的截圖:

widget的擴展更新方法

在上例中widget更新是通過定時方式實現的,在普通情況下這種更新方式已經足夠了,但是對於某些應用使用定時方式更新顯得就不夠用了。比如短信提示,當有新的短信到來時我們希望能夠實時的更新widget,如果還是用定時更新顯然是不行的,那麼能不能讓widget接受除appwidget_update之外的系統消息呢?答案是可以的。

仔細查看下文檔後可以發現,widget只是一個receiver,既然是receiver那麼也就可以接受所有的系統消息了,接下來使用短信提醒示例來演示widget接受系統其他消息的方式,本例中將只前一示例進行修改。

(1)修改AndroidMenifest.xml文件,向其中添加android.provider.Telephony.SMS_RECEIVED監聽事件,代碼如下:

  1. <receiver android:name=".widget" >  
  2.    <intent-filter>  
  3.        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
  4.        <action android:name="android.provider.Telephony.SMS_RECEIVED" />    
  5.    </intent-filter>  
  6.    <meta-data android:name="android.appwidget.provider"  
  7.                     android:resource="@xml/widget_provider" />  
  8. </receiver>  
<receiver android:name=".widget" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/widget_provider" /> </receiver>

添加了這個短信監聽事件後,我們就已經向widget添加了監聽短信的功能了,實際操作非常簡單。

接下來需要添加一個閱讀短信的權限,在AndroidMenifest.xml中任意位置添加<uses-permission android:name="android.permission.RECEIVE_SMS" />

(2)然後還需要修改下widget_setting.xml文件,將其中的updatePeriodMillis屬性設爲0,也就是不定時更新,這樣可以展示這個widget是實時更新的。

(3)之後修改AppWidgetProvider的子類,使其將短信內容顯示到widget上。在本示例中我們將不再修改onUpdate(Context, AppWidgetManager,int[])函數,而是重寫onReceive(Context context, Intent intent)函數,這個函數其實能夠實現包括onUpdate在內的所有函數功能,代碼如下:

  1. package com.dt.time;   
  2.     
  3. import android.appwidget.AppWidgetManager;   
  4. import android.appwidget.AppWidgetProvider;   
  5. import android.content.ComponentName;   
  6. import android.content.Context;   
  7. import android.content.Intent;   
  8. import android.widget.RemoteViews;   
  9.     
  10. public class widget extends AppWidgetProvider {   
  11.       
  12.    @Override  
  13.    public void onReceive(Context context, Intent intent) {   
  14.       // TODO Auto-generated method stub   
  15.       super.onReceive(context, intent);   
  16.          
  17.       //1. 獲取RemoteViews對象   
  18.       RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);   
  19.       //2. 顯示新消息提醒   
  20.       views.setTextViewText(R.id.text, "New message!");   
  21.       //3. 更新widget   
  22.       AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);   
  23.       appWidgetManager.updateAppWidget(new ComponentName(context, widget.class), views);   
  24.    }   
  25. }  
package com.dt.time; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.widget.RemoteViews; public class widget extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub super.onReceive(context, intent); //1. 獲取RemoteViews對象 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main); //2. 顯示新消息提醒 views.setTextViewText(R.id.text, "New message!"); //3. 更新widget AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); appWidgetManager.updateAppWidget(new ComponentName(context, widget.class), views); } }

代碼與上一示例差別不多,唯一有區別的地方便是appWidgetManager的實例化,在上一示例中由於onUpdate()方法中有一個參數是appWidgetManager因此我們不需要單獨實例化該對象,但是在onReceive()方法中並沒有這個參數,因此我們需要實例化一個appWidgetManager,通過AppWidgetManager.getInstance(Context)方法就可以獲得一個appWidgetManager實例。當然由於onReceive()函數還缺少appWidgetIds這個參數,因此我們也不能直接使用updateAppWidget(int[] appWidgetIds, RemoteViews views)這個函數,而要改用updateAppWidget(ComponentName provider, RemoteViews views)這個函數,其中provider參數是一個ComponentName類型的值,簡單的說就是組件名,通過實例化一個組件便可;第二個參數是是一個視圖對象,就是想要展示的widget視圖。

 

通過這樣簡單的改動,當有新的短信息發到手機上時就能第一時間展示在widget中,做到了實時更新。

 

widget的進階更新方法

上面的示例已經讓widget可以實時的更新內容,但是如果要顯示電池電量的話,上面的方法還是不行的,爲什麼呢?原來android中並沒有爲獲取電池信息設計單獨的api,必須註冊爲一個service才能獲取。按照上面的思路那也簡單,只要我們寫個service,然後在系統中廣播更新消息就可以了;但是根據文檔中的說明,要獲取電池信息的service必須是通過Context.registerReceiver()這個函數來註冊一個監聽電池變化的事件才能獲取,這樣一來對我們設計就帶來了麻煩。

解決的方法倒也很簡單,就是單獨寫個service在裏面註冊一個監聽事件,當電池電量發生變化的時候就更新widget,看起來與我們上面的例子很相似,就是多了一個service,但是這裏還有一個不同的是,我們更新widget的方法不再是在onUpdate()或者是onReceive中進行,而是在service中直接對widget修改,似乎跟文檔上說的出入很大,但是看看上面顯示短信的例子我們會發現,在那個示例中似乎widget的更新與appWidgetProvider已經沒有什麼關係了,我們即沒有從參數獲得appWidgetManager實例,也沒從參數中獲得appWidgetIds,一切都是我們自己新建了一個,因而不再widget更新的方法完全是可行的。下面來介紹下代碼:

(1)本示例中沿用第二個示例的代碼,添加一個名叫service.java類

  1. package com.dt.time;   
  2.     
  3. import android.app.Service;   
  4. import android.appwidget.AppWidgetManager;   
  5. import android.content.BroadcastReceiver;   
  6. import android.content.ComponentName;   
  7. import android.content.Context;   
  8. import android.content.Intent;   
  9. import android.content.IntentFilter;   
  10. import android.graphics.Color;   
  11. import android.os.IBinder;   
  12. import android.widget.RemoteViews;   
  13.     
  14. public class mService extends Service {   
  15.     
  16.          
  17.    @Override  
  18.    public void onStart(Intent intent, int startId) {   
  19.    // TODO Auto-generated method stub   
  20.    super.onStart(intent, startId);   
  21.    registerReceiver(this.mBR, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));   
  22.          
  23.    }   
  24.       
  25.    //聲明一個廣播接受對象,用接受電池信息   
  26.    private BroadcastReceiver mBR = new BroadcastReceiver() {   
  27.          
  28.    @Override  
  29.    public void onReceive(Context context, Intent intent) {   
  30.    // TODO Auto-generated method stub   
  31.             
  32.    if(Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction()))   
  33.    {   
  34.             //這裏添加相關處理動作      
  35.       bLevel = intent.getIntExtra("level"0);    //獲取當前電量   
  36.                
  37.       String value = String.valueOf(bLevel) + "%";   //顯示電量的文字           
  38.                
  39.       AppWidgetManager awm = AppWidgetManager.getInstance(context);   
  40.       RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.main);   
  41.                
  42.       views.setTextViewText(R.id.text, value);   
  43.                
  44.       awm.updateAppWidget(new ComponentName(context, widget.class), views);   
  45.          }   
  46.       }   
  47.    };   
  48. }  
package com.dt.time; import android.app.Service; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; import android.os.IBinder; import android.widget.RemoteViews; public class mService extends Service { @Override public void onStart(Intent intent, int startId) { // TODO Auto-generated method stub super.onStart(intent, startId); registerReceiver(this.mBR, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } //聲明一個廣播接受對象,用接受電池信息 private BroadcastReceiver mBR = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub if(Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { //這裏添加相關處理動作 bLevel = intent.getIntExtra("level", 0); //獲取當前電量 String value = String.valueOf(bLevel) + "%"; //顯示電量的文字 AppWidgetManager awm = AppWidgetManager.getInstance(context); RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.main); views.setTextViewText(R.id.text, value); awm.updateAppWidget(new ComponentName(context, widget.class), views); } } }; }

(2)修改AndroidMenifest.xml文件,向其中添加一個<service android:name=".service"/>,這樣就註冊了一個系統服務,當widget啓動的時候這個服務也會跟着啓動,而當widget刪除的時候是否也會自動刪除呢?經過試驗大多數情況下OPhone會自動刪除這個service,如果你爲了安全也可以手動刪除。

之後就可以運行看看效果了,這裏展示的截圖是我作的一個顯示電池電量的widget,目前除了能夠顯示電量外還添加了其他信息的顯示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章