Graham Borland
Graham Borland

Reputation: 60681

Android app widget: content added twice

I am writing an app widget which I intend to populate with a list of items. I'm trying to do it the easy way by extending AppWidgetProvider. I am seeing some strange behaviour where the list of items gets added to the parent widget twice.

Here's the code:

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    Log.i("MyApp", "onUpdate called");

    DbAdapter dbAdapter = new DbAdapter(context);
    dbAdapter.open();

    final String[] columns = { DbTableCategory.KEY_NAME, DbTableCategory.KEY_CURRENTBAL };
    Cursor cursor = dbAdapter.getDb().query(DbTableCategory.TABLE_NAME, columns, null,
                        null, null, null, null);

    final int n = appWidgetIds.length;
    for (int i = 0; i < n; i++) {

        Log.i("MyApp", "Widget instance " + i);

        final int NUM_ITEMS = 4;

        int id = appWidgetIds[i];
        RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);

        for (int c = 0; c < NUM_ITEMS; c++) {

            // get item info from db
            if (cursor.moveToPosition(c)) {
                RemoteViews itemView = new RemoteViews(context.getPackageName(), R.layout.widget_item);
                itemView.setTextViewText(R.id.widget_item_name, cursor.getString(0));
                itemView.setTextViewText(R.id.widget_item_amnt, cursor.getString(1));

                Log.i("MyApp", "Adding subview for item " + c);
                rv.addView(R.id.widget_container, itemView);
            }
        }

        appWidgetManager.updateAppWidget(id, rv);
    }

    cursor.close();
    dbAdapter.close();

I add four items to the list, but I actually see eight items in the widget (the same four appearing twice). From the log output, it is telling me that there are two instances of the widget (according to the appWidgetIds array, and the IDs are different), so the outer loop is running twice, and the inner loop (for each item) is running four times as expected. I don't understand this, as I'm positive I've only added the widget to my home screen once. It's not on any of the other home screens either - I'm using the default HTC Sense launcher.

Even if I had instantiated the widget twice, I'm creating a new RemoteViews for each widget instance. I just don't understand why the one widget instance seems to be receiving two lots of items. What am I getting wrong?

Addendum: when I run the exact same code in the emulator, it works just fine, with just one widget instance being reported. It's only showing the strange behaviour on the actual phone (HTC Desire, Froyo).

Edit: I've since noticed this in the documentation for RemoteViews.addView():

In cases where consumers of RemoteViews may recycle layouts, use removeAllViews(int) to clear any existing children.

Could it be that the same RemoteViews instance is being returned from the RemoteViews(...) constructor? That would explain why the child views are being added twice, but it would not explain why there seem to be two instances in the first place.

Upvotes: 2

Views: 1564

Answers (2)

E_X
E_X

Reputation: 3802

I have faced the same problem, the solution which worked for me is to delete any inner children from the widget_container because when calling rv.removeAllViews(R.id.widget_container); that container should initially have no children in order for no conflicts to happen.

Upvotes: 0

m_vitaly
m_vitaly

Reputation: 11952

The first problem about your widget returning two appWidgetIds: I had similar situations when the widget crash (especially on configuration screens or before the widget was fully placed). The desktop app will still contain information about it, but it won't display it.

The second (main) problem is probably caused by recycling of layout views, my guess is that R.id.widget_container is not cleaned up. So you probably should call:

rv.removeAllViews(R.id.widget_container);

before the for loop.

In addition I would suggest getting the number of returned rows from the cursor (cursor.getCount()) and not hard-coding it (NUM_ITEMS = 4 in your code).

Upvotes: 4

Related Questions