どっこと備忘録群

アウトプットしないとインプットできない私が Androidアプリ開発をメインとした備忘録を載せています。

ウィジェットを実装する

ウィジェット機能を実装したい

実装手順

  1. レイアウトファイルを用意する
  2. ウィジェット用の設定ファイルを追加する
  3. AppWidgetProviderのサブクラスを作成する
  4. AndroidManifest.xmlに追記する

レイアウトを用意する

ウィジェットとして表示するレイアウトファイルを追加する。 通常のレイアウトファイルと同様、res/layoutフォルダに配置する。 ここではwidget_sample_view.xmlとする。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:text="Button"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

ウィジェット用の設定ファイルを追加する

アプリがウィジェット機能を備えていることをOS側に認識させるため、設定ファイルを作成する。 作成したファイルはres/xmlフォルダに配置する。(ここではwidget_provider.xmlとする)

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_sample_view"
    android:minHeight="100dp"
    android:minWidth="200dp"
    android:updatePeriodMillis="1000000" >
</appwidget-provider>

AppWidgetProviderのサブクラスを作成する

AppWidgetProviderを継承したカスタムクラスを作成する。 今回はSampleWidgetProviderとして以下を作成。

class SampleWidgetProvider : AppWidgetProvider() {

  override fun onUpdate(
    context: Context?,
    appWidgetManager: AppWidgetManager?,
    appWidgetIds: IntArray?
  ) {
    context ?: return
    val remoteViews = RemoteViews(context.packageName, R.layout.widget_sample_view)

    val widget = ComponentName(context, SampleWidgetProvider::class.java)
    appWidgetManager?.updateAppWidget(widget, remoteViews)
  }

}

AndroidManifest.xmlに追記する

AndroidManifest.xmlに前項までに追加した設定ファイルとクラスを追記します。receiverタグでSampleWidgetProviderを追加し、ウィジェットのアップデート通知を受け取るためのintent-filter、そして前項で作成した設定ファイルをmeta-dataとしてタグ内に設定します。

  <application
      ...>
      
      <receiver android:name=".SampleWidgetProvider"
          android:exported="true">
          <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
          </intent-filter>

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

  </application>

ここまで実装すれば、作成したウィジェットをホーム画面に配置することができます。

ウィジェット内のViewにクリックリスナーを設定したい

ウィジェット内にクリックリスナーを設定する場合、通常のクリックリスナーと異なり、実装できるのはIntentを発火させるだけ。 このIntentをActivityServiceBroadcastReceiverで受け取り、必要な処理を実施する。 画面を表示したいならActivity、画面を表示せず情報を更新したい場合などはServiceBroadcastReceiverが通知先となる。

たとえば以下の例は、SampleActivityを起動する場合(何らかの画面を表示するケース)。 RemoteViews.setPendingIntentを使って、クリックリスナーを設定したいViewのIDと、 そのViewがタップされたときに発火するIntentPendingIntent)を渡す。

class SampleWidgetProvider : AppWidgetProvider() {

  override fun onUpdate(
    context: Context?,
    appWidgetManager: AppWidgetManager?,
    appWidgetIds: IntArray?
  ) {
    context ?: return
    val remoteViews = RemoteViews(context.packageName, R.layout.widget_sample_view)

    // ボタンがタップされた時のintent(PendingIntent)を設定
    remoteViews.setOnClickPendingIntent(R.id.button, createPendingIntent(context))
    val widget = ComponentName(context, SampleWidgetProvider::class.java)
    appWidgetManager?.updateAppWidget(widget, remoteViews)
  }

  private fun createPendingIntent(context: Context): PendingIntent {
    // SampleActivityを通知先として指定
    val intent = Intent(context, SampleActivity::class.java)  
    // 通知先がActivityなので、PendingIntent.getActivityでPendingIntentを生成。
    // requestCodeやflagは任意の値
    return PendingIntent.getActivity(context, 100, intent, PendingIntent.FLAG_IMMUTABLE)
  }
}

参考

最終更新: 2025.9.7