使用珍藏內容小工具

集合小工具專門顯示許多相同類型的元素,例如 例如圖片庫應用程式的圖片、新聞應用程式的文章 接收來自通訊應用程式的訊息集合小工具通常著重於兩項用途 案例:瀏覽集合,並開啟集合的元素 詳細資料檢視集合小工具可以垂直捲動。

這些小工具會使用 RemoteViewsService 可顯示 遠端資料支援的集合,例如來自內容 供應商。這個小工具會顯示 也就是使用以下其中一種檢視類型 (稱為「集合」) 的資料 資料檢視:

ListView
檢視畫面會在以下位置顯示項目: 垂直捲動清單
GridView
檢視畫面會在以下位置顯示項目: 2D 捲動格線。
StackView
堆疊資訊卡 外觀有如 rolodex,使用者只要滑動畫面正面 上下資訊卡,分別顯示上一張或下一張資訊卡。
AdapterViewFlipper
簡便的轉接器 動畫的 ViewAnimator 兩次以上的檢視畫面之間系統一次只會顯示一名兒童。

這些集合檢視畫面會顯示遠端資料的集合,因此 使用 Adapter 繫結使用者 讓使用者直接存取資料Adapter 會從一組資料中繫結個別項目 套用到個別 View 物件

由於這些集合檢視畫面是由轉接器支援,因此 Android 架構 則必須包含額外架構,以便在小工具中使用。在使用情境 小工具的 Adapter 會由 RemoteViewsFactory, 這是 Adapter 介面周圍的精簡包裝函式。當要求 集合中的特定項目,RemoteViewsFactory 會建立並傳回 做為集合項目 RemoteViews 物件。如要加入 請在小工具內導入 RemoteViewsServiceRemoteViewsFactory

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

這個範例含有十個畫面的堆疊,畫面顯示值為零 預計到九個範例小工具有以下主要行為:

  • 使用者可以在小工具中垂直快速滑過頂部檢視畫面,以顯示下一個 或上一個資料檢視這是內建的 StackView 行為。

  • 不需要任何使用者互動,小工具就會自動推進 就像投影播放一樣這是基於設定 android:autoAdvanceViewId="@id/stack_view"res/xml/stackwidgetinfo.xml 檔案。這項設定適用於資料檢視 ID 在本例中是堆疊檢視畫面的檢視畫面 ID。

  • 如果使用者輕觸頂端檢視畫面,小工具就會顯示 Toast 訊息:「輕觸檢視模式 n」,在哪? 「n」是觸控檢視畫面的索引 (位置)。如要進一步瞭解 如要實作行為,請參閱新增行為至 項目」一節。

透過集合實作小工具

如要實作含有集合的小工具,請按照實作 小工具,後面再幾個步驟: 修改資訊清單、在小工具版面配置中加入集合檢視畫面,並修改 AppWidgetProvider 子類別。

內含集合的小工具資訊清單

除了「宣告小工具 資訊清單,則您需要將含有 要繫結至 RemoteViewsService 的集合。若要執行此操作,請在 您需具備這項權限 BIND_REMOTEVIEWS。 這會防止其他應用程式自由存取小工具的資料。

舉例來說,建立使用 RemoteViewsService 填入 則資訊清單項目可能如下所示:

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

在此範例中,android:name="MyWidgetService" 是指 RemoteViewsService

含有集合的小工具版面配置

小工具版面配置 XML 檔案的主要需求條件是,其中包含以下其中一個 集合檢視畫面:ListViewGridViewStackViewAdapterViewFlipper。以下是 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>

請注意,空白檢視畫面必須是 空白檢視畫面代表空白狀態

除了整個小工具的版面配置檔案之外,請建立其他版面配置 檔案,定義集合中每個項目的版面配置,例如 系列書籍中每本書籍的版面配置。StackWidget 範例有 只有一個項目版面配置檔案 (widget_item.xml),因為所有項目都使用相同的 版面配置。

適用於含有集合的小工具的 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 作為轉接器,將資料整合至遠端集合檢視。

您需要為 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()。您需為以下項目設定待處理意圖範本: 然後將填入意圖設為該資料集內的每個項目 透過「RemoteViewsFactory」收集的資訊。

本節使用 StackWidget 範例,說明如何將行為新增至 像是個別項目在 StackWidget 範例中,如果使用者輕觸頂端檢視畫面, 小工具會顯示 Toast 訊息「Touched view n」。其中 n 是 觸控檢視畫面的索引 (位置)。這類廣告運作方式如下:

  • StackWidgetProviderAppWidgetProvider 子類別— 透過名為 自訂動作的自訂動作建立待處理意圖 TOAST_ACTION

  • 當使用者輕觸檢視畫面時,意圖會觸發並廣播 TOAST_ACTION

  • StackWidgetProvider 類別的 onReceive() 方法,而小工具會顯示 Toast 訊息 也能獲得觸控操作效果產品素材資源集合項目的資料是由 透過 RemoteViewsService 使用 RemoteViewsFactory

,瞭解如何調查及移除這項存取權。

設定待處理意圖範本

StackWidgetProvider ( AppWidgetProvider 子類別) 並設定待處理意圖無法為特定集合中的個別項目進行設定 或自己的待處理意圖而整體集合會設定待處理意圖 每個項目都會設定填入意圖 按商品劃分行為模式

這個類別也會接收在使用者輕觸應用程式時傳送的廣播訊息 檢視畫面。其會在其 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()
);