Android App ウィジェットの作り方

そろそろ、Android 4.0 とかも触りたいなー。でもタブレット買う余裕もないしなーなんて思ってますが、HTC Desire X06HT を入手して、もうすぐ2年たちます。Softbank の2年しばりもそろそろ終わりそうなタイミング。

最近 au に凄く惹かれてきてます。WiMax の料金体系(使ったら500円/月程度、使わなかったら0円) で、テザリングできること(いまだと、PC持ち出しの時には、1day 600円程度でWiMax利用したりしてるのが不要になる)や、コミュファとの合わせ技で、コミュファ代金が安くなったり(現状フレッツで結構いい値段はらってる)や、どうも地元の鉄道路線の各駅にWiFi スポットを開設する予定、などなど。

そんなタイミングに、Android4.0搭載の HTC J の発表と来た。うーん物欲わきます。6月に乗り換えよ。

 

Android 4.0 を待っている間に、ホームスクリーンウィジェットの作り方を覚えて、Android アプリづくりの幅をひろげよー

ということで、今まで触ったことがないので、App WIdget  のページをみて軽く概要をつかんで、、、

って英語で、結構分量ある。。のをめげずに意訳したのが、このページの後半部分。

単純なのをつくるだけなら、以下の手順をとれば良さそう。

App Widget の最低限の作り方

  1. AppWidgetProvider のサブクラスを作成し、onUpdate()メソッドをオーバーライドし、やりたい処理を実装する
  2. レイアウト(XML)を作成。通常通り。ちょっと使えるViewに制約あり。
  3. AppWidgetProviderInfo のメタデータ(XML)を作成し、appwidget-provider 要素の、android:initialLayout に、上記2.で作成したレイアウトを指定し、設定値などを記述する。
  4. AndroidManifest.xml に、receiver 要素を追加し、andorid:nameに、上記1.のAppWidgetProvider のサブクラスを指定、meta-data に、上記3.のAppWidgetProviderInfoのメタデータXMLを指定

これでめでたく、ウィジェットが出てきます。・・・出るだけですが。

まぁ出てしまえば、あとは、ちょっとずつ処理を追加していけばいいでしょう。

appwidget01

 

ということで、以下、来月の自分のために、このページ の前半部分の意訳メモ。

 

基本

Appウィジェットをつくるのに必要なもの

  • AppWidgetProviderInfo オブジェクト
    • Appウィジェットのメタデータを記述する。XMLで定義すべき
  • AppWidgetProvider クラスの実装
    • プログラム可能なインターフェースメソッドを定義。Appウィジェットが更新された場合、ブロードキャストメッセージを受け取る
  • View レイアウト
    • XMLで初期レイアウトを定義

マニフェストに、Appウィジェットを宣言する

最初に、AppWidgetProvider クラスを AndroidManifest.xml に宣言する。

<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>

receiver

android:name 属性必須。Appウィジェットが使用するAppWidgetProviderを指定

intent-filter

android:name を持つ、<action> 要素を含む必要あり。AppWidgetProvider は、ACTION_APPWIDGET_UPDATEブロードキャストを受け取る。明示的に指定しなければいけない唯一のブロードキャスト。

meta-data

AppWidgetProviderInfo のリソースを指定。以下必須。

android:name - メタデータ名。AppWidgetProviderInfoを特定するのに、android.appwidget.provider を使用

android:resource - AppWidgetProviderInfo のリソース場所を指定

AppWidgetProviderInfo メタデータの追加

AppWidgetProviderInfo は、Appウィジェットの、最小サイズ、初期レイアウトリソース、どれくらいの頻度でウィジェットを更新するかのような、本質的な性質を定義する。

/res/xml フォルダに、<appwidget-provider> 要素のみの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/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    android:resizeMode="horizontal|vertical">
</appwidget-provider>

minWidth と minHeight

デフォルトでウィジェットが消費する最小のスペースを指定する。デフォルトのホームスクリーンは、Appウィジェットをグリッドに配置する。最小サイズが、セルのサイズにマッチしない場合、近いセルのサイズに切り上げられる。

ガイドラインを参照

デバイスを問わず、利用できるようにする場合、最小サイズを 4×4セルを超えるサイズにしてはいけない。

updatePeriodMillis

どれくらいの頻度で、Appウィジェットフレームワークが、onUpdate() コールバックメソッド呼び出しによるAppWidgetProvideの更新をリクエストすべきかを定義。

実際の更新は、この値どおりに行われるわけではない。

initialLayout

レイアウトリソースを指定

configure

ユーザーが、Appウィジェットを追加したときに Appウィジェットのプロパティ設定を行わせるために、Activity を起動するかを定義。 オプション。

previewImage

ウィジェットを選択するときなどの、App ウィジェットのプレビューを指定。提供しない場合、アプリケーションのランチャーアイコンが変わりに使用される。

resizeMode

ウィジェットがリサイズできるかのルールを指定する。この属性で、ウィジェットを水平、垂直方向、もしくは両方にリサイズ可能にできる。

App ウィジェット レイアウトの作成

XML で App ウィジェットの初期レイアウトを定義し、res/layout/ ディレクトリに保存しなければならない。以下の View オブジェクト を使って、デザインできる。App ウィジェットデザインガイドラインをデザインを始める前に理解しておくとよい。

App ウィジェットレイアウトの作成は、XML レイアウト と同じ。しかしながら、App ウィジェットレイアウトは、RemoteViews から派生しており、すべての View ウィジェットがサポートされる訳ではないことを留意しておく必要あり。

RemoteView オブジェクトは、次のレイアウトをサポートする。

および、次のウィジェットをサポートする。

これらの派生クラスは、サポートされない。

App ウィジェットにマージンを追加

ウィジェット は、スクリーンの端まで拡がるべきではないし、他のウィジェットにひったり重なるべきではないため、ウィジェットフレーム周りにマージンを取るべき。この強く推奨する振る舞いを利用するためには、アプリケーションの targetSdfkVersion を 14 以降にする。

Android 4.0 では、自動でマージンが取られるため、4.0 以降では、

1.アプリケーションの targetSdfkVersion を 14 以上に

2.以下の様なレイアウトを作成する。この例では、 dimension resource をマージンに使用している

<FrameLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="@dimen/widget_margin">

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="@drawable/my_widget_background">
    …
  </LinearLayout>

</FrameLayout>

3.2つの dimensions resource を作成する。一つは、res/values/ で Android 4.0 以前のためのカスタムマージンで、もう一つは、Android 4.0用 に、res/values-v14/

res/values/

<dimen name="widget_margin">8dp</dimen>

res/values-v14/

<dimen name="widget_margin">0dp</dimen>

AppWidgetProvider クラスの利用

AppWidgetProvider クラスは、BroadcastReceiver を App ウィジェットブロードキャストを取り扱いやすいように継承したもの。App ウィジェットに関連するブロードキャストイベントしか受け取らない。

onUpdate()

AppWidgetProvideInfo の updatePeriodMillis 属性で定めた間隔で、App ウィジェットを更新するために呼び出される。このメソッドは、ユーザーが App ウィジェットを追加した際も呼び出されるので、View へのハンドラの定義や、テンポラリサービスの開始などのセットアップを必要ならば、実行すべき。しかしながら、設定Activity を宣言している場合、Appウィジェットを追加したときに、このメソッドは呼ばれない。その後の更新では呼ばれる。

onDelete(Context, int[])

App ウィジェット ホストから削除されるときには、常に呼び出される。

onEnabled(Context)

App ウィジェットのインスタンスが最初に生成されたときに呼び出される。例えば、あなたの App ウィジェットのインスタンスを2つ追加した場合、最初のみ呼ばれる。一度だけ必要な初期化処理を行うのに都合が良い。

onDisabled(Context)

App ウィジェットの最後のインスタンスが App ウィジェットホストから削除されたときに呼び出される。onEnabled で行った作業のクリーンアップに都合が良い。

onReceive(Context, Intent)

すべてのブロードキャストおよび上記のコールバックメソッドが呼び出される前に呼び出される。AppWidgetProvider が適切にフィルタリングするため、通常このメソッドは実装する必要がない。

最も重要な、コールバックは、onUpdate()。なぜなら、App ウィジェットがホストに追加されたときに毎回呼び出されるから。App ウィジェット がどのようなユーザーインターフェースのイベントを受け入れる場合、イベントハンドラをこのコールバックで登録する必要がある。もし、テンポラリファイルやデータベースを作成しない場合やそのたのクリーンアップ作業を行わない場合、onUpdate() は、定義が必要な唯一のコールバックメソッドとなる。例えば、App ウィジェットにクリックされたら Acitivityを起動するボタンをおきたい場合、AppWidgetProvider に以下の様な実装を行えば良い。

public class ExampleAppWidgetProvider extends AppWidgetProvider {

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

        // このプロバイダに属するすべてのAppウィジェットのための処理
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // ExampleActivity を起動するIntentを生成
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // App ウィジェットのレイアウトを取得し、ボタンにクリックイベントリスナーをアタッチ
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // AppWidgetManager に現在処理しているウィジェットの update を実行するように指示
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

この AppWidgetProvider は、Activity を起動するためのPendingIntent を 定義し、App ウィジェットのボタンのsetOnCllickPendingIntent(int, PendingIntent) にアタッチするために、onUpdate() のみを定義している。このプロバイダにより生成されたすべてのApp ウィジェットのIDの配列、appWidgetIds  分、ループで処理。このようにして、ユーザーが1つ以上のApp ウィジェットのインスタンスを作成した場合、同時に更新できる。しかしながら、1つのupdatrPeriodMillis スケジュールがすべてのインスタンスを管理する。例えば、update スケジュールが2時間毎と定義されている場合、2つめのインスタンスが1つめの1時間後に追加された場合、両方とも、最初のインスタンスのスケジュールで更新される(両方とも、2時間おきに更新される)

App ウィジェット ブロードキャスト Intent を受け取る

AppWidgetProvider は 便利なクラスで、App ウィジェットのブロードキャストを直接受け取りたい場合、独自のBroadcastReceiver を実装するか、onReceive(Context, Intent) コールバックをオーバーライドすれば良い。次の4つの Intent に気を掛ける。