安卓開發之 App Widget

cxmscb發表於2016-08-06

一、什麼是App Widget

Android平臺上在桌面上所放置的一種小控制元件

與App Widget相關的類/物件:

  1. AppWidgetProviderInfo物件:

    定義App Widget的一些資訊,為App Widget提供後設資料,包括App Widget佈局檔案的指定,更新頻率等資料。
    這個物件被定義在XML檔案當中,其相對於一個Config配置“檔案”.
    
  2. AppWidgetProvider:

    繼承自BroadcastReicever廣播接收器。
    定義了App Widget的基本生命週期的回撥函式.
    

二、建立一個App Widget的步驟

一、定義AppWidgetProviderInfo:

  1. res/xml資料夾當中定義描述AppWidgetProviderInfo的xml檔案。

    例:…\res\xml\appwidget_info.xml

    <?xml version="1.0" encoding="utf-8"?>
    <appwidget-provider 
        xmlns:android="http://schemas.android.com/apk/res/android"
    
        android:minHeight="60dp"
        android:minWidth="200dp"
        android:previewImage="@drawable/default_img"
        android:resizeMode="horizontal|vertical"
        android:updatePeriodMillis="40000"
        android:initialLayout="@layout/appwidget_layout" >
    
    </appwidget-provider>
    

    updatePeriodMillis為更新的毫秒數、initialLayout為初始化的widget佈局;
    previewImage為該app widget的預覽圖,在桌面小部件選擇時顯示;
    resizeMode:app widget 在水平和垂直方向是否可以調整大小。

二、為App Widget指定佈局:

  1. 定義App Widget展示時的佈局檔案appwidget_layout

    例:…\res\layout\appwidget_layout.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"
        android:background="#00000000"
        >
    
        <TextView
            android:id="@+id/widget_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="widget text"
            />
    
        <Button
            android:id="@+id/widget_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="widget button"
            />
    
    </LinearLayout>
    

三、實現AppWidgetProvider

  • onUpdate:在到達指定的更新時間之後或者當使用者向桌面新增App Widget時會呼叫該方法

  • onDeleted:在App Widget被刪除時,會呼叫該方法

  • onEnable:當一個App Widget的例項第一次被建立時,會呼叫該方法

  • onDisable:當最後一個App Widget例項被刪除後,會呼叫該方法

  • onReceive:監聽/接收廣播事件,可用於處理控制元件的點選事件等

(桌面上可以有多個同樣的app widget)

例:

public class MyAppWidgetProvider extends AppWidgetProvider {

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
    }

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

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

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
    }

}

因AppWidgetProvider又繼承自BroadcastReceiver,所以需要在AndroidManifest中註冊:

    <receiver android:name=".MyAppWidgetProvider">

        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
        </intent-filter>

        <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/appwidget_info"/>

    </receiver>

其中action是引用android自定義的action,meta-data為後設資料:resource為說明該App Widget 的配置資訊的xml檔案,name必須是android.appwidget.provider。

三、使用App Widget佈局中的控制元件

>使用App Widget佈局中的控制元件其中涉及到了PendingIntent,RemoteViews..

一、PendingIntent

Pending:待處理的。PendingIntent 可以看作為一種“留待日後處理”的意圖,是對Intent的一種包裝,建立之後並不馬上使用,一旦某種觸發事件的發生,意圖才馬上執行。

  1. App Widget和應用程式並不是在同一程式當中。
  2. 建立PendingIntent物件的三個靜態方法:

    • getActivity(Context context, int requestCode, Intent intent, int flags)
    • getBroadcast(Context context, int requestCode, Intent intent, int flags)
    • getService(Context context, int requestCode, Intent intent, int flags)

    根據PeningIntent具體傳送/啟動(廣播/Activity/服務)來選擇對應的一個靜態方法來建立PendingIntent物件。

二、RemoteViews的作用

RemoteViews:遠端控制元件。App Widget裡的控制元件和主程式不在同一程式中,所以App Widget裡的控制元件相對於一種遠端控制元件。

  1. RemoteViews物件表示了一系列的View物件。
  2. RemoteViews所表示的物件執行在另外的程式中。
  3. App Widget當中的View執行在Home Screen程式當中。

三、操作AppWidget的Button

由於AppWidget的Button不與主程式在同一程式中,所以無法按照之前慣用的方法給Button繫結監聽器。

為AppWidget的Button繫結監聽器:

新方法:remoteViews.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
第一個引數為AppWidget的Button的id,第二引數為PendingIntent物件。
當Button被點選時,會引起pendingIntent的執行。

例:重新實現AppWidgetProvider:點選Button時啟動Activity

public class MyAppWidgetProvider extends AppWidgetProvider {

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.i("z","onReceive");
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        // appWidgetMahager:管理app widget
        // appWidgetIds : appWidget的id陣列(桌面可放置多個同樣的小部件)

        for (int i= 0;i< appWidgetIds.length;i++){

            //給每一個小部件的按鈕繫結監聽器
            Log.i("z",appWidgetIds[i]+" ");

            //建立一個Intent物件
            Intent intent = new Intent(context,MainActivity.class);
            //包裝成一個PendingIntent物件
            PendingIntent pendingIntent = PendingIntent.getActivity(context,0,intent,0);

            //得到RemoteViews物件
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidget_layout);
            //給app widget中的Button繫結監聽器
            remoteViews.setOnClickPendingIntent(R.id.widget_button,pendingIntent);

            //更新app widget
            appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);

        }


    }

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

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        Log.i("z","onEnabled");
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        Log.i("z","onDisabled");
    }

}

四、接收來自AppWidget的廣播和更新控制元件的狀態

  1. 在AndroidManifest當中為AppWidgetProvider註冊新的intent-filter,配置所要接收的廣播行為。AppWidgetProvider本質上是一個廣播接收器

  2. 使用getBroadcast(…)方法建立一個傳送廣播PendingIntent。

  3. 為AppWidget當中的控制元件註冊處理器,用來觸發傳送廣播的行為。

  4. 在onReceive方法當中接收來自AppWidget傳送的廣播訊息,並執行自己的業務邏輯,使用RemoteViews物件更新App Widget當中的控制元件狀態。

  5. AppWidgetProvider的執行機制:通過onReceive來接收廣播,接收到特定的系統內建廣播時來呼叫onUpdate(..)等生命週期的回撥方法,由onReceive來呼叫生命週期函式。

例:重新在AndroidMainfest註冊AppWidgerProvider:

    <receiver android:name=".MyAppWidgetProvider">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
        </intent-filter>

        <intent-filter>
            <action android:name="my.appwidget.Text_UPDATE"/>
        </intent-filter>

        <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/appwidget_info"/>

    </receiver>

“android.appwidget.action.APPWIDGET_UPDATE”:系統自帶廣播,用來接收後回撥宣告周期函式。
“my.appwidget.Text_UPDATE”:自定義廣播,用來接收後進行對TextView的操作。

重新實現AppWidgetProvider:

public class MyAppWidgetProvider extends AppWidgetProvider {


    private static final String TEXT_UPDATE = "my.appwidget.Text_UPDATE";

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);

        if(intent!=null&& TextUtils.equals(TEXT_UPDATE,intent.getAction())){

            Log.i("z","onReceiveTextUpdate");

            //獲取app widget的所有控制元件
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidget_layout);

            //設定TextView的文字
            remoteViews.setTextViewText(R.id.widget_text,"Changed Text");

            //獲取app widget的管理器
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

            //獲取app widget的物件
            ComponentName componentName = new ComponentName(context,MyAppWidgetProvider.class);

            //管理器更新app widget
            appWidgetManager.updateAppWidget(componentName,remoteViews);

        }

    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        super.onUpdate(context, appWidgetManager, appWidgetIds);

        // appWidgetMahager:管理app widget
        // appWidgetIds : appWidget的id陣列(桌面小部件可重複放置多個小部件)

        for (int i= 0;i< appWidgetIds.length;i++){

            Log.i("z",appWidgetIds[i]+" ");
            //建立一個傳送廣播的Intent物件,並設定action
            Intent intent = new Intent();
            intent.setAction(TEXT_UPDATE);
            //包裝成一個PendingIntent物件,執行時會傳送一個廣播。
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intent,0);

            //得到RemoteViews物件
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.appwidget_layout);
            //給app widget中的Button繫結監聽器
            remoteViews.setOnClickPendingIntent(R.id.widget_button,pendingIntent);

            //更新app widget
            appWidgetManager.updateAppWidget(appWidgetIds[i],remoteViews);
        }


    }

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

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        Log.i("z","onEnabled");
    }

    @Override
    public void onDisabled(Context context) {
        super.onDisabled(context);
        Log.i("z","onDisabled");
    }

}

相關文章