15

I have an appwidget which uses ListView. I have created a class that extends RemoteViewsService:

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

In my AppWidgetProvider, I call the following method for each instance of the widget (for each appWidgetId):

private static void fillInList(RemoteViews widget, Context context, int appWidgetId) {
    Intent intent = new Intent(context, AppWidgetService.class);
    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    widget.setRemoteAdapter(R.id.appwidget_listview, intent);
}

The constructor of the AppWidgetListFactory

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

    System.out.println("New TVAPWLF for: " + appWidgetId);
    trains = WidgetData.getInstance().widgetMap.get(appWidgetId);
}

Now the problem is, that the fillInList method gets called just as often as there are instances of the widget, but the onGetViewFactory method only gets called once. This results in all the widgets showing the same data.

How can I fix this?

nhaarman
  • 98,571
  • 55
  • 246
  • 278

3 Answers3

36

I haven't tested this myself, but looking at the source code for RemoteViewsService.onBind(), I believe you can not just vary the extras in your Intent in order for it to detect that a new call to your onGetViewFactory() method is needed since it uses the Intent.filterEquals() method for the comparison:

Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.

I would suggest passing the information that you need (widget id?) through the Intent's data instead, maybe something like:

private static void fillInList(RemoteViews widget, Context context, int appWidgetId) {
    Intent intent = new Intent(context, AppWidgetService.class);
    intent.setData(Uri.fromParts("content", String.valueOf(appWidgetId), null));
    widget.setRemoteAdapter(R.id.appwidget_listview, intent);
}

and respectively on the receiving side:

public TreinVerkeerAppWidgetListFactory(Context context, Intent intent) {
    this.context = context;
    appWidgetId = Integer.valueOf(intent.getData().getSchemeSpecificPart());

    System.out.println("New TVAPWLF for: " + appWidgetId);
    trains = WidgetData.getInstance().widgetMap.get(appWidgetId);
}
Joe
  • 14,039
  • 2
  • 39
  • 49
  • @Joe can u please check how to do this in my code, actually i made the changes as u said but now it crashes. This is my code http://pastie.org/7063960 and this is the logcat http://pastie.org/7063968 – AndroidDev Mar 22 '13 at 08:19
  • @Anshuman I see a problem on line 36 on your first pastie: `String.valueOf(appWidgetIds)`. Here `appWidgetIds` is an array instead of a single integer and it produces an invalid URI in the intent (something like "[I@424b7d58" instead of "2"). Do you meant to write `String.valueOf(appWidgetIds[0])` instead? – Joe Mar 22 '13 at 16:21
  • @Joe Yep its working now. I have one more question. Is it possible to set background image to the app widget listView inside the code, instead of setting it in the xml file. – AndroidDev Mar 25 '13 at 09:11
  • @Anshuman It would probably be best if you post a new question since that seems to be an unrelated problem. Make sure you do a search to see if anyone has asked/answered something similar first :) – Joe Mar 25 '13 at 15:53
  • Wow, this solution almost literally saved my weekend of paranoia and frustration, I have the similar problem here but now the case is closed :) https://stackoverflow.com/questions/46400576/remoteviewfactory-ondatasetchanged-only-called-once-per-notifyappwidgetviewdat – Andrew Lam Sep 25 '17 at 21:37
  • But what if I need to send 3 arrays of strings for example? – c0dehunter Jul 04 '19 at 16:49
  • Wow, such a good answer! Thank you. Your answer works, thumbs up for that! – DFJ Jun 22 '20 at 19:33
3

Based on the above description another trick you can do if you are setting intent extra other than string could be:

Random random = new Random();
intent.setType(String.valueOf(random.nextInt(1000)));

This will invoke a call to onGetViewFactory();

amit srivastava
  • 743
  • 6
  • 25
0

to invoke a call to onGetViewFactory ( recreate the widget )

val appWidgetManager = AppWidgetManager.getInstance(context)
appWidgetManager.updateAppWidget(appWidgetId, remoteView)

make sure the intent has different parameter that sent before

surga
  • 1,436
  • 21
  • 25