AndroidのWidget開発

この記事は、まだ書きかけです。

前置き

DevGuideの"App Widgets"などを読みつつ、要点をメモしていきます。

この記事は、まだ書きかけです。

本文

Widgetとは

  • 他アプリ(HOMEアプリとか)に埋め込めるview
  • viewを提供する側がApp Widget provider
  • viewを受け入れる側がApp Widget host

基本

  • xmlでviewのレイアウトを定義
  • android.appwidget.AppWidgetProviderInfoが、xmlで記述された、widgetのメタ情報を保持
  • android.appwidget.AppWidgetProviderの派生クラスで、widgetへのイベント(broadcast event)を受け取る
  • AppWidgetProviderは、BoradcastReceiverの派生クラス
  • setting用のActivityを実装しても良い

マニフェストにAppWidgetProviderを定義

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>

widgetのメタ情報を定義

  • res/xml/example_appwidget_info
<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/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure"
    android:autoAdvanceViewId="@id/example_appwidget_view
    android:resizeMode="horizontal|vertical">
</appwidget-provider>
  • HOMEアプリのセルが74x74dpと仮定して4セル分の幅を確保
  • ピクセル変換時の誤差を考慮して2dp引く
  • onUpdate()を呼ぶ周期を24時間に設定
  • この周期が厳密に守られるわけではない
  • 複数のインスタンスがある場合は、最初のインスタンスの周期に合わせて全インスタンスのonUpdate()が呼ばれる
  • システムがスリープ中なら起こす
  • 起こしたくない場合は、onUpdate()は使わず、android.app.AlarmManagerを使うべき
  • ユーザがwidgetを選択するときのプレビュー画像を登録(デフォルトはアプリアイコン)
  • ユーザがwidgetを貼ったとき、ExampleAppWidgetConfigure(Activity)が開く
  • autoAdvanceViewIdは、Collectionベースのwidgetにおいて、データ表示先となる子view(ListViewかGridViewかStackViewかAdapterViewFlipper)のIDを指定するために使う
  • リサイズを許さないならnoneを指定する

widgetのレイアウトを定義

  • res/layout/example_appwidget.xml
  • widgetのレイアウトはandroid.widget.RemoteViews
  • 使えるのは、FrameLayout、LinearLayout、RelativeLayoutのみ
  • 子viewは、AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper

AppWidgetProviderの派生クラスを定義

  • onUpdate()は、周期的に呼ばれる
  • ユーザがwidgetをaddしたときにも呼ばれる(setting用のActivityがある場合は呼ばれない)
  • onDeleted()は、ユーザがwidgetを削除したときに呼ばれる
  • onEnabled()は、widgetの最初のインスタンスが生成されたときに呼ばれる
  • 複数インスタンスをaddした場合、2個目以降は呼ばれない
  • onDesabled()は、最後のインスタンスを削除したときに呼ばれる
  • onReceive()は、オーバーライドする必要はない
public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

onUpdate()でモタモタするとANRが出る。

AppWidgetProviderを使わずに、素のBrowadcastRecieverのonReceive()で下記イベントを処理してもいい。

  • ACTION_APPWIDGET_UPDATE
  • ACTION_APPWIDGET_DELETED
  • ACTION_APPWIDGET_ENABLED
  • ACTION_APPWIDGET_DISABLED

setting用のActivity

  • App Widget hostから呼ばれるので、APPWIDGET_CONFIGUREインテントをキャッチできるようにマニフェストに登録する
<activity android:name=".ExampleAppWidgetConfigure">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>
  • インテントのEXTRA_APPWIDGET_IDエクストラにwidget IDが付与されている
  • setResult()で結果を返すこと
  • setting用のActivityがある場合、add時のonUpdate()がスキップされるので、Activityの中でwidgetをupdateする
  • 結果にRESULT_CANCELEDをセットすると、widgetはaddされない
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
    mAppWidgetId = extras.getInt(
            AppWidgetManager.EXTRA_APPWIDGET_ID, 
            AppWidgetManager.INVALID_APPWIDGET_ID);
}

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);

Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();

Collectionベースのwidget

Last modified:2011/07/11 15:31:53
Keyword(s):
References:[Androidアプリ開発]
This page is frozen.