LoginSignup
1

More than 5 years have passed since last update.

[Android] AppWidgetProviderで複数のクリックイベントを扱う

Last updated at Posted at 2018-05-03

はじめに

ウィジェットのクリックイベントの処理は特殊で,ブロードキャストを行うことで実装します.この記事では複数のクリックイベントを扱う際の注意点と,Oreoからの暗黙的なブロードキャストの制限についての対策を書いていきます.

※ ウィジェットのクリックイベントの処理方法をある程度知っていることが前提の記事です

PendingIntentの作成

ここで注意すべきことは次の3つです.

  • Intentを明示的にする
  • requestCodeを一意にする
  • ブロードキャスト受け取り時の処理分岐を可能にする

以下のコードでIntentrequestCodeの作成をします.

private enum class ClickType {
     TYPE1, TYPE2, TYPE3
}

private fun createIntentAndRequestCode(context: Context, appWidgetId: Int, type: ClickType): Pair<Intent, Int>{
    val intent = Intent(context, WidgetCounter::class.java).apply {
        putExtra(APP_WIDGET_ID, appWidgetId)
        putExtra(CLICK_TYPE, type)
    }
    val requestCode = appWidgetId * ClickType.values().size + type.ordinal
    return Pair(intent, requestCode)
}

private const val CLICK_TYPE = "clickType"
private const val APP_WIDGET_ID = "appWidgetId"

その後,PendingIntent.getBroadcast()でブロードキャストするPendingIntentを生成し,RemoteViews.setOnClickPendingIntent()でクリックが発生した時の処理をセットします.

val (intent, code) = createIntentAndRequestCode(context, appWidgetId, ClickType.TYPE1)
// viewsはウィジェット本体(RemoteViews)
views.setOnClickPendingIntent(R.id.layout_id, // クリックイベントを設定するviewのID
    PendingIntent.getBroadcast(context, code, intent, PendingIntent.FLAG_UPDATE_CURRENT))

Intentを明示的にする

Oreoから暗黙的なブロードキャストに制限がかかるため,自身(AppWidgetProvider)に対して明示的なブロードキャストする必要があります.Intent(context, WidgetCounter::class.java)の様にクラスを直接指定します.

requestCodeを一意にする

ブロードキャストするPendingIntentには一意なrequestCodeを割り当てる必要があります.ウィジェットの一意なIDであるappWidgetIdを利用することで,一意なrequestCodeを生成することが出来ます.

val requestCode = appWidgetId * ClickType.values().size + type.ordinal

後述するClickTypeが異なればappWidgetIdが同じでもrequestCodeが区別出来るようにするため,ClickTypeごとにtype.ordinalで値をずらしています.

ブロードキャスト受け取り時の処理分岐を可能にする

処理分岐のためにenum classであるClickTypeを定義します.Intの定数を複数定義して,その値によって処理を分岐させても良いのですが,enum classの方が便利なのでこちらを利用しました.IntentputExtra(CLICK_TYPE, type)ClickTypeを渡して,ブロードキャスト受け取り時に分岐が出来るようにします.

onReceiveでイベント処理

onReceive では,クリックイベント以外のブロードキャストも受け取ってしまう点に注意します.ウィジェットの更新を行うACTION_APPWIDGET_UPDATEなどのIntentも受け取り,スーパークラスで処理されるため,フィルタリングをする必要があります.

override fun onReceive(context: Context, intent: Intent) {
    super.onReceive(context, intent) // ACTION_APPWIDGET_UPDATEなどのIntentが処理される
    if (!intent.hasExtra(APP_WIDGET_ID) || !intent.hasExtra(CLICK_TYPE)) return

    val appWidgetId = intent.getIntExtra(APP_WIDGET_ID, 0)
    when (intent.getSerializableExtra(CLICK_TYPE) as ClickType) {
        ClickType.TYPE1 -> {
            // 処理1
        }
        ClickType.TYPE2 -> {
            // 処理2
        }
        ClickType.TYPE3 -> {
            // 処理3
        }
    }
}

おわりに

明示的なブロードキャストという不思議なことをしていますが,これでOreoでも動くようになります.同じrequestCodeを利用したために,処理分岐が出来ないというミスで数時間無駄にしてしまったので,同様の原因で詰まっている人の助けになればいいなと思います.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1