このページでは、ユーザー エクスペリエンスを向上させる、より高度なウィジェットを作成する際のおすすめの方法について説明します。
ウィジェット コンテンツの更新の最適化
ウィジェットのコンテンツを更新すると、計算コストが高くなる可能性があります。バッテリーの消費を抑えるには、アップデートの種類、頻度、タイミングを最適化します。
ウィジェットの更新の種類
ウィジェットを更新するには、完全な更新、部分更新、コレクション ウィジェットの場合はデータの更新の 3 つの方法があります。コンピューティングの費用と影響は、それぞれ異なります。
以下では、各更新タイプについて説明し、それぞれのコード スニペットを示します。
フル アップデート:
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
を呼び出して、ウィジェットを完全に更新します。これにより、以前に提供されたRemoteViews
が新しいRemoteViews
に置き換えられます。これは最も計算コストの高い更新です。Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also { setTextViewText(R.id.textview_widget_layout1, "Updated text1") setTextViewText(R.id.textview_widget_layout2, "Updated text2") } appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout); remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1"); remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2"); appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
部分更新:
AppWidgetManager.partiallyUpdateAppWidget
を呼び出して、ウィジェットの一部を更新します。これにより、新しいRemoteViews
が以前に指定されたRemoteViews
とマージされます。ウィジェットがupdateAppWidget(int[], RemoteViews)
を通じて少なくとも 1 つのフル アップデートを受信しない場合、このメソッドは無視されます。Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also { setTextViewText(R.id.textview_widget_layout, "Updated text") } appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout); remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text"); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
コレクション データの更新:
AppWidgetManager.notifyAppWidgetViewDataChanged
を呼び出して、ウィジェットのコレクション ビューのデータを無効にします。これにより、RemoteViewsFactory.onDataSetChanged
がトリガーされます。それまでの間、古いデータがウィジェットに表示されます。このメソッドを使用すると、コストの高いタスクを同期的に安全に実行できます。Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
これらのメソッドは、アプリの UID が対応する AppWidgetProvider
クラスと同じであれば、アプリ内のどこからでも呼び出すことができます。
ウィジェットを更新する頻度を決定する
ウィジェットは、updatePeriodMillis
属性に指定された値に応じて定期的に更新されます。ウィジェットは、ユーザーの操作、ブロードキャスト更新、またはその両方に応じて更新できます。
定期的に更新する
定期的な更新の頻度を制御するには、appwidget-provider
XML で AppWidgetProviderInfo.updatePeriodMillis
の値を指定します。更新のたびに AppWidgetProvider.onUpdate()
メソッドがトリガーされ、ウィジェットを更新するコードを配置できます。ただし、ウィジェットでデータを非同期で読み込む必要がある場合や、更新に 10 秒以上かかる場合は、次のセクションで説明するブロードキャスト レシーバの更新の代替方法を検討してください。これは、10 秒経過すると BroadcastReceiver
が応答していないと認識されるためです。
updatePeriodMillis
は 30 分未満の値に対応していません。ただし、定期的な更新を無効にする場合は、0 を指定します。
管理者は、ユーザーが構成の更新頻度を調整できるようにすることができます。たとえば、株式相場を 15 分ごとや 1 日 4 回のみ更新したい場合があります。この場合、updatePeriodMillis
を 0 に設定して、代わりに WorkManager
を使用します。
ユーザーの操作に応じて更新する
ユーザーの操作に応じてウィジェットを更新するためのおすすめの方法を以下に示します。
アプリのアクティビティから: ユーザーのタップなどのユーザー操作に応じて、
AppWidgetManager.updateAppWidget
を直接呼び出します。通知やアプリ ウィジェットなどのリモート操作から:
PendingIntent
を作成し、呼び出されたActivity
、Broadcast
、Service
からウィジェットを更新します。優先度はご自身で選択できます。たとえば、PendingIntent
にBroadcast
を選択した場合、フォアグラウンド ブロードキャストを選択して、BroadcastReceiver
を優先させることができます。
ブロードキャスト イベントに応じて更新する
ウィジェットの更新が必要なブロードキャスト イベントの例として、ユーザーが写真を撮ることが挙げられます。この場合は、新しい写真が検出されたときにウィジェットを更新します。
JobScheduler
でジョブをスケジュールし、JobInfo.Builder.addTriggerContentUri
メソッドを使用してブロードキャストをトリガーとして指定できます。
また、ブロードキャストの BroadcastReceiver
を登録することもできます。たとえば、ACTION_LOCALE_CHANGED
をリッスンできます。ただし、デバイスのリソースを消費するため、慎重に使用し、特定のブロードキャストのみをリッスンしてください。Android 7.0(API レベル 24)と Android 8.0(API レベル 26)で導入されたブロードキャストの制限により、特定の例外を除いて、アプリはマニフェストに暗黙的なブロードキャストを登録できません。
BroadcastReceiver からウィジェットを更新する際の考慮事項
ウィジェットを AppWidgetProvider
を含む BroadcastReceiver
から更新する場合は、ウィジェットの更新期間と優先度について、次の点に注意してください。
更新の期間
原則として、ブロードキャスト レシーバ(通常はアプリのメインスレッドで実行される)は最大 10 秒間実行されてから、応答していないと判断され、アプリケーション応答なし(ANR)エラーがトリガーされます。ウィジェットの更新に時間がかかる場合は、次の代替手段を検討してください。
WorkManager
を使用してタスクのスケジュールを設定します。goAsync
メソッドを使用して、レシーバの時間を長くします。これにより、レシーバーは 30 秒間実行できます。
詳しくは、セキュリティ上の考慮事項とベスト プラクティスをご覧ください。
アップデートの優先度
デフォルトでは、ブロードキャスト(AppWidgetProvider.onUpdate
を使用して作成されたものを含む)はバックグラウンド プロセスとして実行されます。つまり、システム リソースが過負荷状態になると、ブロードキャスト レシーバの呼び出しが遅延する可能性があります。ブロードキャストを優先するには、フォアグラウンド プロセスにします。
たとえば、ユーザーがウィジェットの特定の部分をタップしたときに PendingIntent.getBroadcast
に渡される Intent
に Intent.FLAG_RECEIVER_FOREGROUND
フラグを追加します。
動的アイテムを含む正確なプレビューを作成する
![](https://developer.android.google.cn/static/images/appwidgets/missing-list.png?authuser=7&hl=ja)
このセクションでは、コレクション ビューを持つウィジェット、つまり ListView
、GridView
、または StackView
を使用するウィジェットのウィジェット プレビューに複数のアイテムを表示するためのおすすめの方法について説明します。
ウィジェットでこれらのビューのいずれかを使用している場合、実際のウィジェット レイアウトを直接提供することでスケーラブルなプレビューを作成すると、ウィジェット プレビューにアイテムが表示されないときにエクスペリエンスが低下します。これは、コレクション ビューデータが実行時に動的に設定され、図 1 の画像のように見えるためです。
コレクション ビューを持つウィジェットのプレビューをウィジェット選択ツールで適切に表示するには、プレビュー専用の個別のレイアウト ファイルを保持することをおすすめします。この個別のレイアウト ファイルには、実際のウィジェット レイアウトと、架空のアイテムを含むプレースホルダ コレクション ビューが含まれています。たとえば、複数の疑似リストアイテムを含むプレースホルダ LinearLayout
を指定することで、ListView
を模倣できます。
ListView
の例を示すために、別のレイアウト ファイルから始めます。
// res/layout/widget_preview.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_background"
android:orientation="vertical">
// Include the actual widget layout that contains ListView.
<include
layout="@layout/widget_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// The number of fake items you include depends on the values you provide
// for minHeight or targetCellHeight in the AppWidgetProviderInfo
// definition.
<TextView android:text="@string/fake_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
<TextView android:text="@string/fake_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
</LinearLayout>
AppWidgetProviderInfo
メタデータの previewLayout
属性を指定するときに、プレビュー レイアウト ファイルを指定します。実際のウィジェット レイアウトは initialLayout
属性に指定し、実行時に RemoteViews
を作成するときに実際のウィジェット レイアウトを使用します。
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
複雑なリストアイテム
前のセクションの例では、リストアイテムが TextView
オブジェクトであるため、架空のリストアイテムを提供しています。アイテムが複雑なレイアウトの場合、架空のアイテムを提供するのはより複雑になる可能性があります。
widget_list_item.xml
で定義され、2 つの TextView
オブジェクトで構成されるリストアイテムについて考えてみましょう。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
架空のリストアイテムを提供するには、レイアウトを複数回含めることができますが、その場合、各リストアイテムは同一になります。一意のリストアイテムを指定する手順は次のとおりです。
テキスト値の属性セットを作成します。
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
次の属性を使用してテキストを設定します。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetTitle" /> <TextView android:id="@id/content" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetContent" /> </LinearLayout>
プレビューに必要な数のスタイルを作成します。各スタイルの値を再定義します。
<resources> <style name="Theme.Widget.ListItem"> <item name="widgetTitle"></item> <item name="widgetContent"></item> </style> <style name="Theme.Widget.ListItem.Preview1"> <item name="widgetTitle">Fake Title 1</item> <item name="widgetContent">Fake content 1</item> </style> <style name="Theme.Widget.ListItem.Preview2"> <item name="widgetTitle">Fake title 2</item> <item name="widgetContent">Fake content 2</item> </style> </resources>
プレビュー レイアウトの架空のアイテムにスタイルを適用します。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ...> <include layout="@layout/widget_view" ... /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview1" /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview2" /> </LinearLayout>