App Widget

zxc123e發表於2015-11-28

1. 基本資訊

App Widget是一種可以被放在其他應用中(如Launcher)並接收週期性更新的應用檢視。這些檢視在UI上就表現為Widget,並且你可以同App Widget Provider一起釋出。
要建立一個App Widget,你需要完成以下步驟:

  • AppWidgetProviderInfo物件:它描述了App
    Widget的基本元素,比如說佈局、更新頻率、AppWidgetProvider類等。這些都是在xml檔案中定義的。
  • 2.AppWidgetProvider類的實現:它定義了一些基本的方法以支援通過廣播事件與App Widget互動。通過它,當App Widget被更新、啟用、禁用以及刪除時,你將收到相應的廣播資訊。
  • 3.View Layout:通過xml檔案定義App Widget的初始檢視。

2. 新增App Widget佈局

另外,你還可以實現一個App Widget的配置Activity。當然,這不是強制的。
要建立你的App Widget的初始佈局,你可以使用以下View物件。
建立佈局不是很麻煩,重點是,你必須記住,這個佈局是基於RemoteViews的,不是所有的佈局型別與View都支援。
一個RemoteViews物件可以支援以下佈局類:

FrameLayoutLinearLayoutRelativeLayout
以及一下widget類
AnalogClockButtonChronometerImageButton
ImageViewProgressBarTextViewViewFlipper

注:這些類的子類是不支援的。
res/layout資料夾下建立widget.xml檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
                android:text="當前時間" android:textColor="@android:color/white" android:textSize="18sp" android:id="@+id/tv_weather"/>
</LinearLayout>

3. 新增AppWidgetProviderInfo後設資料

res/xml資料夾下建立weather.xml檔案

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
    android:minWidth="294dp"  
    android:minHeight="72dp"  
    android:updatePeriodMillis="86400000"  
    android:previewImage="@drawable/preview"  
    android:initialLayout="@layout/widget"  
    android:configure="com.example.android.ExampleAppWidgetConfigure"   
    android:resizeMode="horizontal|vertical">  
</appwidget-provider>  
  • minWidth與minHeight屬性表示了App Widget所需的最小布局區域。
    預設的主屏中,App Widget要想確認其位置,需要通過基於網格的具有固定寬度和高度的單元。如果App Widget的最小寬度和高度無法匹配給定的單元,它將會自動擴充套件到最接近的單元大小。
    由於主屏的佈局方向是可變的,你應該考慮最壞的情況(每單元的寬和高都是74dp)。然而,為了防止在擴充套件時產生整數計算錯誤,你還需要減去2。因此,你可以用以下公式來計算最小寬度和高度(單位dp):(單元數量×74)-2。
    同時,為了保證你的App Widget能夠在各種裝置上正常使用,它們的寬度和高度必須不超過4×4個單元。

  • updatePeriodMillis屬性定義了App
    Widget框架呼叫AppWidgetProvider的onUpdate方法的頻率。對於實際的更新,我們不建議採用該值進行實時處理。最好是越不頻繁越好——為了保證電量,一小時不超過一次為好。當然,你也可以允許使用者對更新頻率進行設定。
    注意,如果更新觸發時裝置正處於休眠狀態,裝置將喚醒以執行該操作。如果你的更新頻率不超過一小時一次,這不會對電池的壽命產生多大的影響。但如果你需要更頻繁地更新卻又不想要在裝置休眠時執行,那你可以使用定時器來執行更新。要達到這種目的,可以在AlarmManager中設定一個AppWidgetProvider能接收的Intent。將型別設為ELAPSED_REALTIME或RTC。由於AlarmManager只有當裝置處於喚醒狀態時才會被呼叫,我們只要設updatePeriodMillis為0即可。

  • linitialLayout屬性標識了初始佈局檔案。
  • configure屬性定義了當使用者新增App Widget時呼叫的Activity。(這是可選的)
  • previewImage定義了App
    Widget的縮圖,當使用者從widget列表中選擇時,顯示的就是這張圖。如果沒設定,使用者將看見的是你的應用的預設圖示。
  • autoAdvanceViewId屬性是在Android3.0引入的,用於標識需要被host(launcher)自動更新的widget的子檢視。
  • resizeMode屬性標識了widget重新佈局的規則。你可以使用該屬性來讓widget能夠在水平、豎直、或兩個方向上均可變化。可用的值包括horizontal、vertical、none。如果是想在兩個方向上均能拉伸,可設定為horizontal|vertical,當然,需要Android3.1以上版本。

4. 使用AppWidgetProvider類

public class ClockProvider extends AppWidgetProvider {
    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        Intent _intent = new Intent(context, ClockService.class);
        context.startService(_intent);
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        Intent _intent = new Intent(context, ClockService.class);
        context.stopService(_intent);
    }
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }

}
  • onUpdate()
    由updatePeriodMills定義的時間間隔觸發。當然,新增Widget的時候也會,因此,應該在此處執行一些必要的配置,如定義View的事件處理handler,有必要的話,還會啟動一個臨時的Service。然而,如果你宣告瞭一個配置Activity,該方法將不會在此時被呼叫。

  • onDelete(Context, int[])
    當App Widget從host中移除時會被呼叫。

  • onEnabled(Context)
    當App Widget的例項被第一次建立時,該方法將被呼叫。如果你建了兩個例項,那該方法也只會被呼叫一次。
  • onDisabled(Context)
    當最後一個App Widget的例項被刪除時,該方法被呼叫。你可以在此處清除之前在onEnabled裡執行的操作。

  • onReceive(Content, Intent)
    每次接收到廣播都會被呼叫,而且執行的順序在上述方法之前。通常,你不需要實現該方法,因為AppWidgetProvider已經對各種不同型別的廣播進行了過濾及分發。
    注:在Android1.5中,有一個已知的問題,使得在某些情況下,onDeleted不會被呼叫。這時,你就需要實現onReceive()了。

5. 使用Service更新資料

public class ClockService extends Service implements AMapLocalWeatherListener {
    private LocationManagerProxy mLocationManagerProxy;
    public SimpleDateFormat sdf;
    public Timer timer;
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        System.out.println("start");
        sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {

                String time = sdf.format(new Date());
                updateViews(time);
            }
        },0,1000);
    }


    private void updateViews(String info)
    {
        Intent _intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, _intent, 0);
        RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget);
        views.setTextViewText(R.id.tv_weather, info);
        views.setOnClickPendingIntent(R.id.tv_weather, pendingIntent);//點選跳到主頁
        AppWidgetManager manager = AppWidgetManager.getInstance(this);
        ComponentName name = new ComponentName(this, ClockProvider.class);
        manager.updateAppWidget(name,views);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        timer.cancel();
    }
}

通過RemoteViews類和AppWidgetManager類傳送廣播更新介面資料

6.在AndroidManifest中宣告一個App Widget

<service android:name=".ClockService"></service>
        <receiver android:name=".ClockProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
            </intent-filter>
            <meta-data
                    android:name="android.appwidget.provider"
                    android:resource="@xml/weather"></meta-data>
        </receiver>

meta-data標籤標識了AppWidgetProviderInfo資源,它需要以下屬性:
android:name:使用android.appwidget.provider來標識AppWidgetProviderInfo。
android:resource:標識AppWidgetProviderInfo的資源位置。

相關文章