ラベル ウィジェット の投稿を表示しています。 すべての投稿を表示
ラベル ウィジェット の投稿を表示しています。 すべての投稿を表示

2025年4月26日土曜日

Android ウィジェット機能の実装方法を解説

どうも。どっことです。今回はAndroidのウィジェット機能の実装方法について解説します。

ウィジェット機能

iOSと大きく違うところとして、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に前項までに追加した設定ファイルとクラスを追記します。receivertタグでSampleWidgetProviderを追加し、Widgetのアップデート通知を受け取るための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がタップされたときに発火するIntent(PendingIntent)を渡します。

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

まとめ

今回はAndroidのウィジェット機能の実装方法について解説しました。開発したアプリを利用してもらうための導線としてもウィジェットは機能するので、利用する際は参考にしていただけると幸いです。

参考

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。