Android:如何强制更新特定类型的所有小部件

Chr*_*dus 13 android widget

我已经看到了很多这样的问题,我一遍又一遍地看到同样的答案.我不想为我的应用程序拥有的每种小部件都有一个服务,特别是因为我的应用程序已经拥有2个持久服务.

具体来说,如果我的一个现有服务看到数据已更改,我想更新我的小部件.在计时器上执行此操作很烦人,因为它可能是更新之间的天数,或者在一小时内可能有几天.我希望我的小部件始终显示最新信息.

Android小部件设计似乎是基于您的小部件在需要时提取信息的基础上工作,我认为有许多明智的场景,其中活动可能希望将数据推送到小部件.

阅读下面的答案,了解我是如何做到这一点的.据我所知,如果做得好,没有任何不利影响.

Chr*_*dus 37

要强制更新特定窗口小部件提供程序的窗口小部件,我们需要执行以下操作:

  • 设置我们的提供商以接收专门的广播
  • 发送专业广播:
    1. 所有当前窗口小部件Id与提供者关联
    2. 要发送的数据
    3. 只有我们的提供商响应的密钥(重要!)
  • 让我们的小部件点击更新(没有服务!)

第1步 - 设置我们的AppWidgetProvider

我不会详细介绍如何创建info xml文件或更改为Android Manifest - 如果你不知道如何正确创建一个小部件,那么你应该先阅读很多教程.

这是一个示例AppWidgetProvider类:

public class MyWidgetProvider extends AppWidgetProvider {

public static final String WIDGET_IDS_KEY ="mywidgetproviderwidgetids";
public static final String WIDGET_DATA_KEY ="mywidgetproviderwidgetdata";

}

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.hasExtra(WIDGET_IDS_KEY)) {
        int[] ids = intent.getExtras().getIntArray(WIDGET_IDS_KEY);
        if (intent.hasExtra(WIDGET_DATA_KEY)) {
           Object data = intent.getExtras().getParcelable(WIDGET_DATA_KEY);
           this.update(context, AppWidgetManager.getInstance(context), ids, data);
        } else {
            this.onUpdate(context, AppWidgetManager.getInstance(context), ids);
        }
    } else super.onReceive(context, intent);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) {
    update(context, appWidgetManager, appWidgetIds, null);
}

//This is where we do the actual updating
public void update(Context context, AppWidgetmanager manager, int[] ids, Object data) {

    //data will contain some predetermined data, but it may be null
   for (int widgetId : ids) {
        .
        .
        //Update Widget here
        .
        .
        manager.updateAppWidget(widgetId, remoteViews);
    }
}
Run Code Online (Sandbox Code Playgroud)

第2步 - 发送广播

在这里,我们可以创建一个静态方法,让我们的小部件更新.重要的是我们使用自己的键和小部件更新操作,如果我们使用AppWidgetmanager.EXTRA_WIDGET_IDS,我们不仅会破坏我们自己的小部件,还会破坏其他小部件.

public static void updateMyWidgets(Context context, Parcelable data) {
    AppWidgetManager man = AppWidgetManager.getInstance(context);
    int[] ids = man.getAppWidgetIds(
            new ComponentName(context,MyWidgetProvider.class));
    Intent updateIntent = new Intent();
    updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_ID_KEY, ids);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_DATA_KEY, data);
    context.sendBroadcast(updateIntent);
}
Run Code Online (Sandbox Code Playgroud)

如果将此方法与多个提供程序一起使用,请确保它们使用不同的密钥.否则,您可能会发现widget a的代码更新小部件b,这可能会产生一些奇怪的后果.

第3步 - 点击更新

另一件好事是让我们的小部件在点击时进行恶意更新.将以下代码添加到update方法中以实现此目的:

RemoteViews views = 
      new RemoteViews(context.getPackageName(),R.layout.mywidget_layout);

Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(myWidgetProvider.WIDGET_IDS_KEY, ids);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
      context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

views.setOnClickPendingIntent(R.id.view_container, pendingIntent);
Run Code Online (Sandbox Code Playgroud)

此代码将导致在单击窗口小部件时调用onUpdate方法.

笔记

  • 对updateWidgets()的任何调用都将导致更新我们窗口小部件的所有实例.
  • 单击小部件将导致更新我们小部件的所有实例
  • 无需服务
  • 当然,请注意不要经常更新 - 小部件更新使用电池电量.
  • 请记住,所有窗口小部件提供程序都会收到广播,而我们的特殊键确保它只是我们实际更新的窗口小部件.


xba*_*esx 6

很抱歉OP的答案迟到了,但对于其他人来说,您可以使用这样的方法来更新特定类型的所有应用程序小部件(布局和类):

public static void updateAllWidgets(final Context context,
                                    final int layoutResourceId,
                                    final Class< ? extends AppWidgetProvider> appWidgetClass)
{
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), layoutResourceId);

    AppWidgetManager manager = AppWidgetManager.getInstance(context);
    final int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, appWidgetClass));

    for (int i = 0; i < appWidgetIds.length; ++i)
    {
        manager.updateAppWidget(appWidgetIds[i], remoteViews);
    }
}
Run Code Online (Sandbox Code Playgroud)

它的作用是RemoteViews为您的布局创建一个 ,然后获取AppWidgetManager并找到您提供的类的所有应用程序小部件,迭代它们并更新它们。

  • 我只想指出,每当您更新“RemoteViews”时,您就(或者至少应该将其视为)重新启动“RemoteViews”。如果您只想更新图像,并保持所有其他属性不变,那么您最好也(重新)设置所有其他属性,否则您将丢失它们。 (2认同)