コレクション ウィジェットを使用する

コレクション ウィジェットは、次のような、同じタイプの多くの要素の表示に特化しています。 ギャラリー アプリの画像のコレクション、ニュースアプリの記事、 やり取りできます。コレクション ウィジェットでは通常、以下の 2 つの用途に重点を置きます。 ケース: コレクションの閲覧、コレクションの要素をそのコレクションで開く できます。コレクション ウィジェットは上下にスクロールできます。

これらのウィジェットでは 表示する RemoteViewsService リモート データに基づくコレクション( できます。ウィジェットには、 コレクションと呼ばれるビュータイプのいずれかを使用して、 ビュー:

ListView
特定のフォルダ内でアイテムを表示するビュー 縦方向にスクロールするリストです。
GridView
特定のフォルダ内でアイテムを表示するビュー 2 次元のスクロール グリッドです。
StackView
縦に積まれたカード 前面をフリックすると、Rolodex のようなビューです。 前または次のカードを表示するために、カードを上または下にスクロールします。
AdapterViewFlipper
アダプターを使用する(シンプル) アニメーション化する ViewAnimator ビュー間の調整も可能ですお子様は一度に 1 人だけ表示されます。

これらのコレクション ビューは、リモートデータに基づくコレクションを表示するため、 Adapter を使用してユーザーをバインドする データへのインターフェースを提供します。Adapter は、データセットから個々のアイテムをバインドします。 個々の View オブジェクトに割り当てることもできます。

これらのコレクション ビューはアダプターを基盤としているため、Android フレームワークは ウィジェットでの使用をサポートするための追加のアーキテクチャが必要です。コンテキスト ウィジェットの場合、AdapterRemoteViewsFactory, Adapter インターフェースのシンラッパーです。リクエストが 場合、RemoteViewsFactory が作成して返します。 コレクションのアイテムとして RemoteViews オブジェクト。コメントに コレクション ビューを作成し、RemoteViewsService を実装して、 RemoteViewsFactory

RemoteViewsService は、リモート アダプターによるリクエストを可能にするサービスです。 RemoteViews オブジェクト。RemoteViewsFactory はアダプターのインターフェースです。 コレクション ビュー(ListViewGridViewStackView、およびそのビューの基になるデータです。StackWidgetより サンプル、 このサービスを実装するボイラープレート コードの例を以下に示します。 インターフェース:

Kotlin

class StackWidgetService : RemoteViewsService() {

    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory {
        return StackRemoteViewsFactory(this.applicationContext, intent)
    }
}

class StackRemoteViewsFactory(
        private val context: Context,
        intent: Intent
) : RemoteViewsService.RemoteViewsFactory {

// See the RemoteViewsFactory API reference for the full list of methods to
// implement.

}

Java

public class StackWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

// See the RemoteViewsFactory API reference for the full list of methods to
// implement.

}

サンプルアプリ

このセクションのコード抜粋も、StackWidget サンプル:

図 1. StackWidget

このサンプルは、値 0 を表示する 10 個のビューのスタックで構成されています。 表示されます。サンプル ウィジェットの主な動作は次のとおりです。

  • ユーザーはウィジェットの上面を垂直方向にフリングして次のビューを表示できます。 確認できます。これは組み込みの StackView の動作です。

  • ユーザーが操作しなくても、ウィジェットは自動的に スライドショーのように順番に表示できます。これは android:autoAdvanceViewId="@id/stack_view"res/xml/stackwidgetinfo.xml ファイル。この設定はビュー ID に適用されます。 これはスタックビューのビュー ID です

  • ユーザーがトップビューをタップすると、ウィジェットには Toast 「タッチビュー n」というメッセージが表示されるここで n は、タップされたビューのインデックス(位置)です。Google Cloud のオペレーションスイートを 動作を実装するには、動作を個々に items セクションにあります。

コレクションを使用してウィジェットを実装する

コレクションを使用するウィジェットを実装するには、次の手順に沿って、 ウィジェットを実行してから、さらにいくつかの手順を実施します。 マニフェストの変更、ウィジェット レイアウトへのコレクション ビューの追加、 AppWidgetProvider サブクラス。

コレクションを含むウィジェットのマニフェスト

マニフェストを使用して、ウィジェットが RemoteViewsService にバインドするコレクションを作成します。そのためには、Terraform で 権限を使用してマニフェスト ファイルに BIND_REMOTEVIEWS。 これにより、他のアプリはウィジェットのデータに自由にアクセスできなくなります。

たとえば、RemoteViewsService を使用して マニフェスト エントリは次のようになります。

<service android:name="MyWidgetService"
    android:permission="android.permission.BIND_REMOTEVIEWS" />

この例では、android:name="MyWidgetService"RemoteViewsService

コレクションを使用したウィジェットのレイアウト

ウィジェット レイアウト XML ファイルの主な要件は、 コレクション ビュー: ListViewGridViewStackView、または AdapterViewFlipper。これは、次の widget_layout.xml ファイルです。 StackWidget サンプル:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <StackView
        android:id="@+id/stack_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:loopViews="true" />
    <TextView
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:background="@drawable/widget_item_background"
        android:textColor="#ffffff"
        android:textStyle="bold"
        android:text="@string/empty_view_text"
        android:textSize="20sp" />
</FrameLayout>

空のビューは、 empty view は、空の状態を表します。

ウィジェット全体のレイアウト ファイルに加えて、別のレイアウトを作成します。 コレクション内の各アイテムのレイアウトを定義するファイル(例: 書籍のコレクション内の各書籍のレイアウト。StackWidget サンプルには次のものがあります。 すべてのアイテムが同じものを使用するため、アイテム レイアウト ファイル widget_item.xml を 1 つだけにする できます。

コレクションを持つウィジェットの AppWidgetProvider クラス

通常のウィジェットと同様に、 AppWidgetProvider サブクラス 一般的に、 onUpdate()。 作成する際の onUpdate() の実装の主な違いは、 コレクションを含むウィジェットでは、 setRemoteAdapter()。これにより、データを取得する場所をコレクション ビューに伝えます。 その後、RemoteViewsService が次の実装を返します。 RemoteViewsFactory に定義することで、ウィジェットが適切なデータを提供できます。ユーザーが このメソッドを呼び出す場合は、 RemoteViewsService と、更新するウィジェットを指定するウィジェット ID。

たとえば、StackWidget サンプルで onUpdate() を実装すると次のようになります。 このコールバック メソッドで、RemoteViewsService を ウィジェット コレクション:

Kotlin

override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
) {
    // Update each of the widgets with the remote adapter.
    appWidgetIds.forEach { appWidgetId ->

        // Set up the intent that starts the StackViewService, which
        // provides the views for this collection.
        val intent = Intent(context, StackWidgetService::class.java).apply {
            // Add the widget ID to the intent extras.
            putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
        }
        // Instantiate the RemoteViews object for the widget layout.
        val views = RemoteViews(context.packageName, R.layout.widget_layout).apply {
            // Set up the RemoteViews object to use a RemoteViews adapter.
            // This adapter connects to a RemoteViewsService through the
            // specified intent.
            // This is how you populate the data.
            setRemoteAdapter(R.id.stack_view, intent)

            // The empty view is displayed when the collection has no items.
            // It must be in the same layout used to instantiate the
            // RemoteViews object.
            setEmptyView(R.id.stack_view, R.id.empty_view)
        }

        // Do additional processing specific to this widget.

        appWidgetManager.updateAppWidget(appWidgetId, views)
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds)
}

Java

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    // Update each of the widgets with the remote adapter.
    for (int i = 0; i < appWidgetIds.length; ++i) {

        // Set up the intent that starts the StackViewService, which
        // provides the views for this collection.
        Intent intent = new Intent(context, StackWidgetService.class);
        // Add the widget ID to the intent extras.
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        // Instantiate the RemoteViews object for the widget layout.
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        // Set up the RemoteViews object to use a RemoteViews adapter.
        // This adapter connects to a RemoteViewsService through the specified
        // intent.
        // This is how you populate the data.
        views.setRemoteAdapter(R.id.stack_view, intent);

        // The empty view is displayed when the collection has no items.
        // It must be in the same layout used to instantiate the RemoteViews
        // object.
        views.setEmptyView(R.id.stack_view, R.id.empty_view);

        // Do additional processing specific to this widget.

        appWidgetManager.updateAppWidget(appWidgetIds[i], views);
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}

データを永続化する

このページで説明するように、RemoteViewsService サブクラスには次の機能が用意されています。 リモート コレクション ビューの入力に使用される RemoteViewsFactory

具体的には、次の手順を行います。

  1. サブクラス RemoteViewsServiceRemoteViewsService は、サービスである リモート アダプタが RemoteViews をリクエストできます。

  2. RemoteViewsService サブクラスに、 RemoteViewsFactory インターフェース。RemoteViewsFactory は リモート コレクション ビュー(ListViewGridViewStackView、およびそのビューの基になるデータ。お客様の 実装により、各オブジェクトの RemoteViews オブジェクトが作成されます。 表します。このインターフェースは Adapter のシンラッパーです。

サービスの単一のインスタンス、またはそこに含まれるデータを使用して、 維持します。静的でない限り、RemoteViewsService にはデータを保存しないでください。条件 ウィジェットのデータを維持するには、 ContentProvider のデータが 存続します。たとえば食料品店ウィジェットでは 食料品リストの各アイテムの状態を、サーバー ストレージなどの SQL データベースです

RemoteViewsService 実装の主な内容は、 RemoteViewsFactory。次のセクションで説明します。

RemoteViewsFactory インターフェース

RemoteViewsFactory インターフェースを実装するカスタムクラスでは、 そのコレクション内のアイテムのデータが表示されます。そのために、 は、ウィジェット アイテムの XML レイアウト ファイルをデータのソースと結合します。この データはデータベースから単純な配列まで何でもかまいません。StackWidget サンプルの場合、データソースは WidgetItems の配列です。RemoteViewsFactory データをリモート コレクション ビューに接着するためのアダプターとして機能します。

診断用の問題に実装する必要がある 2 つの最も重要なメソッドは、 RemoteViewsFactory サブクラスは onCreate() および getViewAt()

初めてファクトリーを作成するときに、システムによって onCreate() が呼び出されます。 ここでデータソースへの接続やカーソルを設定します。対象 たとえば、StackWidget サンプルでは、onCreate() を使用して、 WidgetItem オブジェクト。ウィジェットがアクティブな場合、システムは 配列内のインデックス位置を使用してオブジェクトを表示し、 できます。

以下は、StackWidget サンプルの RemoteViewsFactory からの抜粋です。 onCreate() メソッドの一部を示す実装です。

Kotlin

private const val REMOTE_VIEW_COUNT: Int = 10

class StackRemoteViewsFactory(
        private val context: Context
) : RemoteViewsService.RemoteViewsFactory {

    private lateinit var widgetItems: List<WidgetItem>

    override fun onCreate() {
        // In onCreate(), set up any connections or cursors to your data
        // source. Heavy lifting, such as downloading or creating content,
        // must be deferred to onDataSetChanged() or getViewAt(). Taking
        // more than 20 seconds on this call results in an ANR.
        widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") }
        ...
    }
    ...
}

Java

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static final int REMOTE_VIEW_COUNT = 10;
    private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>();

    public void onCreate() {
        // In onCreate(), setup any connections or cursors to your data
        // source. Heavy lifting, such as downloading or creating content,
        // must be deferred to onDataSetChanged() or getViewAt(). Taking
        // more than 20 seconds on this call results in an ANR.
        for (int i = 0; i < REMOTE_VIEW_COUNT; i++) {
            widgetItems.add(new WidgetItem(i + "!"));
        }
        ...
    }
...

RemoteViewsFactory メソッド getViewAt()RemoteViews オブジェクトを返します。 (データセット内の指定された position のデータに対応する)を返します。こちらの StackWidget サンプルの RemoteViewsFactory 実装からの抜粋:

Kotlin

override fun getViewAt(position: Int): RemoteViews {
    // Construct a remote views item based on the widget item XML file
    // and set the text based on the position.
    return RemoteViews(context.packageName, R.layout.widget_item).apply {
        setTextViewText(R.id.widget_item, widgetItems[position].text)
    }
}

Java

public RemoteViews getViewAt(int position) {
    // Construct a remote views item based on the widget item XML file
    // and set the text based on the position.
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_item);
    views.setTextViewText(R.id.widget_item, widgetItems.get(position).text);
    return views;
}

個々のアイテムに動作を追加する

前のセクションでは、データをウィジェット コレクションにバインドする方法について説明しました。しかし、 個々のアイテムに動的な動作を追加する場合は、 ありますか?

onUpdate() を使用してイベントを処理する クラスでは、 setOnClickPendingIntent(): オブジェクトのクリック動作を設定します。たとえば、 ボタンにより Activity が起動されます。しかし、 個々のコレクション アイテムの子ビューに対して、この方法を使用することはできません。 たとえば、setOnClickPendingIntent() を使用してグローバル ボタンを設定できます。 たとえば、アプリを起動する Gmail ウィジェットには表示されますが、 リストアイテムごとに異なります。

代わりに、コレクション内の個々のアイテムにクリック動作を追加するには、 setOnClickFillInIntent()。この作業では、VM のペンディング インテント テンプレートを コレクション ビューに追加してから、コレクション ビュー内の商品アイテムごとに RemoteViewsFactory のコレクション。

このセクションでは、StackWidget サンプルを使用して、動作を追加する方法について説明します。 表示されます。StackWidget のサンプルでは、ユーザーが上部ビューにタッチすると、 ウィジェットには「タッチビュー n」というメッセージ Toast が表示されます。ここで、n は タッチされたビューのインデックス(位置)。次のような仕組みになっています。

  • StackWidgetProvider - AppWidgetProvider サブクラス - 次のカスタム アクションを持つペンディング インテントを作成します。 TOAST_ACTION

  • ユーザーがビューをタップすると、インテントが起動してブロードキャスト TOAST_ACTION

  • このブロードキャストは、StackWidgetProvider クラスの onReceive() メソッドにより、ウィジェットに Toast メッセージが表示されます。 クリックします。コレクション項目のデータは、 RemoteViewsService 経由で RemoteViewsFactory

で確認できます。

ペンディング インテント テンプレートをセットアップする

StackWidgetProviderAppWidgetProvider サブクラス) ペンディング インテントを設定します。コレクションの個々のアイテムに 別のペンディング インテントを使用します。代わりに、コレクション全体でペンディング インテントが設定されます。 各アイテムが入力インテントを設定して、 動作をアイテムごとに指定できます。

このクラスは、ユーザーがタップしたときに送信されるブロードキャストも受け取ります。 表示されます。このイベントを onReceive() メソッドで処理します。インテントの アクションが TOAST_ACTION の場合、ウィジェットは現在の「Toast」というメッセージを表示します。 表示されます。

Kotlin

const val TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION"
const val EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM"

class StackWidgetProvider : AppWidgetProvider() {

    ...

    // Called when the BroadcastReceiver receives an Intent broadcast.
    // Checks whether the intent's action is TOAST_ACTION. If it is, the
    // widget displays a Toast message for the current item.
    override fun onReceive(context: Context, intent: Intent) {
        val mgr: AppWidgetManager = AppWidgetManager.getInstance(context)
        if (intent.action == TOAST_ACTION) {
            val appWidgetId: Int = intent.getIntExtra(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID
            )
            // EXTRA_ITEM represents a custom value provided by the Intent
            // passed to the setOnClickFillInIntent() method to indicate the
            // position of the clicked item. See StackRemoteViewsFactory in
            // Set the fill-in Intent for details.
            val viewIndex: Int = intent.getIntExtra(EXTRA_ITEM, 0)
            Toast.makeText(context, "Touched view $viewIndex", Toast.LENGTH_SHORT).show()
        }
        super.onReceive(context, intent)
    }

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Update each of the widgets with the remote adapter.
        appWidgetIds.forEach { appWidgetId ->

            // Sets up the intent that points to the StackViewService that
            // provides the views for this collection.
            val intent = Intent(context, StackWidgetService::class.java).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                // When intents are compared, the extras are ignored, so embed
                // the extra sinto the data so that the extras are not ignored.
                data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
            }
            val rv = RemoteViews(context.packageName, R.layout.widget_layout).apply {
                setRemoteAdapter(R.id.stack_view, intent)

                // The empty view is displayed when the collection has no items.
                // It must be a sibling of the collection view.
                setEmptyView(R.id.stack_view, R.id.empty_view)
            }

            // This section makes it possible for items to have individualized
            // behavior. It does this by setting up a pending intent template.
            // Individuals items of a collection can't set up their own pending
            // intents. Instead, the collection as a whole sets up a pending
            // intent template, and the individual items set a fillInIntent
            // to create unique behavior on an item-by-item basis.
            val toastPendingIntent: PendingIntent = Intent(
                    context,
                    StackWidgetProvider::class.java
            ).run {
                // Set the action for the intent.
                // When the user touches a particular view, it has the effect of
                // broadcasting TOAST_ACTION.
                action = TOAST_ACTION
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))

                PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT)
            }
            rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent)

            appWidgetManager.updateAppWidget(appWidgetId, rv)
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds)
    }
}

Java

public class StackWidgetProvider extends AppWidgetProvider {
    public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";
    public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM";

    ...

    // Called when the BroadcastReceiver receives an Intent broadcast.
    // Checks whether the intent's action is TOAST_ACTION. If it is, the
    // widget displays a Toast message for the current item.
    @Override
    public void onReceive(Context context, Intent intent) {
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        if (intent.getAction().equals(TOAST_ACTION)) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
            // EXTRA_ITEM represents a custom value provided by the Intent
            // passed to the setOnClickFillInIntent() method to indicate the
            // position of the clicked item. See StackRemoteViewsFactory in
            // Set the fill-in Intent for details.
            int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
            Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
        }
        super.onReceive(context, intent);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Update each of the widgets with the remote adapter.
        for (int i = 0; i < appWidgetIds.length; ++i) {

            // Sets up the intent that points to the StackViewService that
            // provides the views for this collection.
            Intent intent = new Intent(context, StackWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            // When intents are compared, the extras are ignored, so embed
            // the extras into the data so that the extras are not
            // ignored.
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);

            // The empty view is displayed when the collection has no items. It
            // must be a sibling of the collection view.
            rv.setEmptyView(R.id.stack_view, R.id.empty_view);

            // This section makes it possible for items to have individualized
            // behavior. It does this by setting up a pending intent template.
            // Individuals items of a collection can't set up their own pending
            // intents. Instead, the collection as a whole sets up a pending
            // intent template, and the individual items set a fillInIntent
            // to create unique behavior on an item-by-item basis.
            Intent toastIntent = new Intent(context, StackWidgetProvider.class);
            // Set the action for the intent.
            // When the user touches a particular view, it has the effect of
            // broadcasting TOAST_ACTION.
            toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);
            toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
            rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);

            appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
        }
        super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
}

フィルイン インテントを設定する

RemoteViewsFactory で、 ありますこれにより、個々のクリック アクションを区別できます。 グループ化します入力インテントが PendingIntent テンプレート: アイテムがタップされたときに実行される最終的なインテント。

Kotlin

private const val REMOTE_VIEW_COUNT: Int = 10

class StackRemoteViewsFactory(
        private val context: Context,
        intent: Intent
) : RemoteViewsService.RemoteViewsFactory {

    private lateinit var widgetItems: List<WidgetItem>
    private val appWidgetId: Int = intent.getIntExtra(
            AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID
    )

    override fun onCreate() {
        // In onCreate(), set up any connections or cursors to your data source.
        // Heavy lifting, such as downloading or creating content, must be
        // deferred to onDataSetChanged() or getViewAt(). Taking more than 20
        // seconds on this call results in an ANR.
        widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") }
        ...
    }
    ...

    override fun getViewAt(position: Int): RemoteViews {
        // Construct a remote views item based on the widget item XML file
        // and set the text based on the position.
        return RemoteViews(context.packageName, R.layout.widget_item).apply {
            setTextViewText(R.id.widget_item, widgetItems[position].text)

            // Set a fill-intent to fill in the pending intent template.
            // that is set on the collection view in StackWidgetProvider.
            val fillInIntent = Intent().apply {
                Bundle().also { extras ->
                    extras.putInt(EXTRA_ITEM, position)
                    putExtras(extras)
                }
            }
            // Make it possible to distinguish the individual on-click
            // action of a given item.
            setOnClickFillInIntent(R.id.widget_item, fillInIntent)
            ...
        }
    }
    ...
}

Java

public class StackWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static final int count = 10;
    private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>();
    private Context context;
    private int appWidgetId;

    public StackRemoteViewsFactory(Context context, Intent intent) {
        this.context = context;
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    // Initialize the data set.
    public void onCreate() {
        // In onCreate(), set up any connections or cursors to your data
        // source. Heavy lifting, such as downloading or creating
        // content, must be deferred to onDataSetChanged() or
        // getViewAt(). Taking more than 20 seconds on this call results
        // in an ANR.
        for (int i = 0; i < count; i++) {
            widgetItems.add(new WidgetItem(i + "!"));
        }
        ...
    }

    // Given the position (index) of a WidgetItem in the array, use the
    // item's text value in combination with the widget item XML file to
    // construct a RemoteViews object.
    public RemoteViews getViewAt(int position) {
        // Position always ranges from 0 to getCount() - 1.

        // Construct a RemoteViews item based on the widget item XML
        // file and set the text based on the position.
        RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_item);
        rv.setTextViewText(R.id.widget_item, widgetItems.get(position).text);

        // Set a fill-intent to fill in the pending
        // intent template that is set on the collection view in
        // StackWidgetProvider.
        Bundle extras = new Bundle();
        extras.putInt(StackWidgetProvider.EXTRA_ITEM, position);
        Intent fillInIntent = new Intent();
        fillInIntent.putExtras(extras);
        // Make it possible to distinguish the individual on-click
        // action of a given item.
        rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);

        // Return the RemoteViews object.
        return rv;
    }
    ...
}

コレクション データを最新の状態に保つ

図 2 は、コレクションを使用するウィジェットの更新フローを示しています。内容 ウィジェット コードが RemoteViewsFactory とやり取りする方法と、 更新をトリガーします。

図 2. 更新中の RemoteViewsFactory とのやり取り。

コレクションを使用するウィジェットでは、ユーザーに最新のコンテンツを提供できます。対象 たとえば Gmail ウィジェットでは、受信トレイの状況が一目でわかります。これを実現するには、 RemoteViewsFactory とコレクション ビューをトリガーして、 表示されます。

これを行うには、 AppWidgetManager で発信 notifyAppWidgetViewDataChanged()。この呼び出しにより、RemoteViewsFactory オブジェクトの onDataSetChanged() メソッドを使用すると、新しいデータをフェッチできます。

処理負荷の高いオペレーションは、同じプロジェクト内で同期的に実行できます。 onDataSetChanged() コールバック。この通話は確実に終了します メタデータまたはビューデータが RemoteViewsFactory から取得される前に通知を受け取ります。マイページ getViewAt() 内で処理負荷の高いオペレーションを実行することもできます。 メソッドを呼び出します。この呼び出しに時間がかかる場合、 RemoteViewsFactory オブジェクト getLoadingView() メソッド - コレクション ビューの対応する位置に表示されます。 返されます。

RemoteCollectionItems を使用してコレクションを直接渡す

Android 12(API レベル 31)では、setRemoteAdapter(int viewId, RemoteViews.RemoteCollectionItems items) が追加されています。 メソッドを使用すると、アプリで値を設定するときに、アプリがコレクションを直接渡すことができます。 コレクションビューが表示されます。この方法でアダプタを設定した場合、 RemoteViewsFactory を実装します。このインターフェースを notifyAppWidgetViewDataChanged()

この方法では、アダプターへのデータ入力が簡単になるだけでなく、 ユーザーがリストを下にスクロールしたときに新しいアイテムが入力されるまでの遅延を解消します。 新しいアイテムを表示します。以下の条件においては、この方法が推奨されます。 コレクション アイテムのセットが比較的少ない場合です。ただし、たとえば、 コレクションに多数の Bitmaps が含まれていると、アプローチがうまくいきません。 setImageViewBitmap に渡されます。

コレクションで一定のレイアウト セットを使用していない場合は、 一部のアイテムのみ存在する場合は、setViewTypeCount を使用して コレクションに含めることができる一意のレイアウトの最大数。これにより、 アプリ ウィジェットを更新する際に再利用できます。

簡素化された RemoteViews コレクションを実装する方法の例を次に示します。

Kotlin

val itemLayouts = listOf(
        R.layout.item_type_1,
        R.layout.item_type_2,
        ...
)

remoteView.setRemoteAdapter(
        R.id.list_view,
        RemoteViews.RemoteCollectionItems.Builder()
            .addItem(/* id= */ ID_1, RemoteViews(context.packageName, R.layout.item_type_1))
            .addItem(/* id= */ ID_2, RemoteViews(context.packageName, R.layout.item_type_2))
            ...
            .setViewTypeCount(itemLayouts.count())
            .build()
)

Java

List<Integer> itemLayouts = Arrays.asList(
    R.layout.item_type_1,
    R.layout.item_type_2,
    ...
);

remoteView.setRemoteAdapter(
    R.id.list_view,
    new RemoteViews.RemoteCollectionItems.Builder()
        .addItem(/* id= */ ID_1, new RemoteViews(context.getPackageName(), R.layout.item_type_1))
        .addItem(/* id= */ ID_2, new RemoteViews(context.getPackageName(), R.layout.item_type_2))
        ...
        .setViewTypeCount(itemLayouts.size())
        .build()
);