Как обновить виджет приложения с представлением списка из действия

Я знаю, что об этом спрашивали много раз, но я просмотрел документацию сверху вниз, прочитал все ответы здесь, и ни один из них не помог. Честно говоря, каждый ответ говорит что-то свое о том, как к этому подойти.

Теперь вернемся к моему вопросу. Я хочу обновить представление списка виджетов из какой-то активности, и для этой цели я создал WidgetProvider#sendUpdateBroadcastToAllWidgets(), который я вызываю из активности.

В конечном итоге он вызывает onUpdate(), поэтому широковещательная передача принимается правильно. Но взгляды не обновляются.

Я также пытался вызвать AppWidgetManager#notifyAppWidgetViewDataChanged() и обновить данные в WidgetFactory#onDataSetChanged(), но метод так и не был вызван.

Итак, я предполагаю, что все это не работает, потому что фабрика удаленных представлений кэшируется, но я не знаю, как надежно это преодолеть. Есть предположения?

А что насчет контекстов? Я всегда должен поставлять один, но мне все равно, какой именно. Это имеет значение?

Спасибо

ПОСТАВЩИК

public class WidgetProvider extends AppWidgetProvider {

    public static void sendUpdateBroadcastToAllWidgets(Context context) {
        int allWidgetIds[] = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, WidgetProvider.class));
        Intent intent = new Intent(context, WidgetProvider.class);
        intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);
        context.sendBroadcast(intent);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager widgetManager, int[] widgetIds) {
        for (int id : widgetIds) {
            updateWidget(context, widgetManager, id);
        }
        super.onUpdate(context, widgetManager, widgetIds);
    }

    @Override
    public void onDeleted(Context context, int[] widgetIds) {
        WidgetPreferences prefs = new WidgetPreferences(context);
        for (int widgetId : widgetIds) {
            prefs.getWidgetPreferences(widgetId).edit().clear().commit();
        }
        super.onDeleted(context, widgetIds);
    }

    private static void updateWidget(Context context, AppWidgetManager widgetManager, int widgetId) {
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);

        // set list adapter
        Intent serviceIntent = new Intent(context, WidgetService.class);
        serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
        serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)));
        views.setRemoteAdapter(android.R.id.list, serviceIntent);
        views.setEmptyView(android.R.id.list, android.R.id.empty);

        // set widget title
        WidgetDataCategory category = new WidgetPreferences(context).getSavedCategory(widgetId);
        views.setTextViewText(R.id.titleText, context.getString(category.titleResourceId()));

        // set onclick listener - we create a pending intent template and when an items is clicked
        // the intent is filled with missing data and sent
        Intent startActivityIntent = new Intent(context, SimplePersonDetailActivity.class);
        startActivityIntent.setData(Uri.parse(startActivityIntent.toUri(Intent.URI_INTENT_SCHEME)));
        startActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        startActivityIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, startActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setPendingIntentTemplate(android.R.id.list, pendingIntent);

        // all hail to Google
        widgetManager.updateAppWidget(widgetId, views);
    }
}

ЗАВОД

public class WidgetFactory implements RemoteViewsService.RemoteViewsFactory {

    private Context context;
    private List<Person> people = new ArrayList<>();

    public WidgetFactory(Context context, Intent intent) {
        this.context = context;

        int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
            WidgetPreferences prefs = new WidgetPreferences(context);

            WidgetDataCategory category = prefs.getSavedCategory(widgetId);
            int numberOfItemsToShow = prefs.getSavedLimit(widgetId);

            people = category.filterAndSlice(new PersonDao(context).getAllForGroup(Constants.SIMPLE_GROUP_ID), numberOfItemsToShow);
        }
    }

    @Override
    public void onCreate() {}

    @Override
    public void onDataSetChanged() {}

    @Override
    public void onDestroy() {}

    @Override
    public int getCount() {
        return people.size();
    }

    @Override
    public RemoteViews getViewAt(int position) {
        Person person = people.get(position);
        BigDecimal amount = ListViewUtil.sumTransactions(new TransactionDao(context).getAllForPerson(person));

        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.item_widget);
        remoteViews.setTextViewText(R.id.nameText, person.getName());
        remoteViews.setTextViewText(R.id.amountText, MoneyFormatter.withoutPlusPrefix().format(amount));

        // fill details for the onclick listener (updating the pending intent template
        // set in the WidgetProvider)
        Intent listenerIntent = new Intent();
        listenerIntent.putExtra(Constants.PERSON_ID, people.get(position).getId());
        remoteViews.setOnClickFillInIntent(R.id.widgetItem, listenerIntent);

        return remoteViews;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

person user219882    schedule 12.03.2016    source источник


Ответы (2)


Я бы сказал, что метод notifyAppWidgetViewDataChanged должен работать для вас.

Вы должны построить AppWidgetManager и получить appWidgetIds, а затем просто позвонить notifyAppWidgetViewDataChanged на свой AppWidgetManager.

Псевдокод,

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetIds[] = appWidgetManager.getAppWidgetIds(
                           new ComponentName(context, WidgetProvider.class));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listview);

Для получения дополнительной информации вы можете проверить мой ответ здесь, который содержит демонстрацию на github.

person Lalit Poptani    schedule 15.03.2016
comment
Должно быть, у меня плохая карма или что-то в этом роде. Я делал это раньше, и это не сработало, но я все равно попробовал еще раз, и это сработало :) Спасибо - person user219882; 15.03.2016
comment
И еще один момент, недостаточно просто сообщить, что данные изменились. Это само по себе не работает для нескольких экземпляров виджета. Также нужно вызвать updateWidget() или вообще тот код, который есть в onUpdate() - person user219882; 17.03.2016

У меня была реализация виджета в моем проекте. Я изменил приведенный ниже код, чтобы данные в виджете можно было изменить из одного из моих действий в приложении. Показан только основной код для вашего конкретного варианта использования.

Вот у меня есть кнопка с текстом в моем виджете. Нажав кнопку входа в свою активность, я изменяю текст кнопки в своем виджете

Ниже приведен мой класс AppWidgetProvider.java

public class AppWidgetTrackAsset extends AppWidgetProvider{
      @Override
      public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each App Widget that belongs to this provider
        final int N = appWidgetIds.length;

        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch Activity
            Intent intent = new Intent(context, WidgetAlertActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.app_widget_track_asset);
            views.setOnClickPendingIntent(R.id.sosButton, pendingIntent);
            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        Log.v(Constants.WIDGET_LOG, "onReceive called with " + intent.getAction());
        if (intent.getAction().equals("update_widget")) {
            // widget update started
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
                    R.layout.app_widget_track_asset);
            // Update text , images etc
            remoteViews.setTextViewText(R.id.sosButton, "My updated text");
            // Trigger widget layout update
            AppWidgetManager.getInstance(context).updateAppWidget(
                    new ComponentName(context, AppWidgetTrackAsset.class), remoteViews);
        }
    }
    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }
}


Ниже показана моя активность, в которой я обновляю текст кнопки виджета при нажатии кнопки входа
LoginActivity.java

public class LoginActivity extends AppCompatActivity implements View.OnClickListener{

  Button loginButton;
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        loginButton=(Button)findViewById(R.id.loginButton);
        loginButton.setOnClickListener(this);

    }
    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.loginButton){
            updateWidget();
        }
    }
    private void updateWidget(){
        try {
            Intent updateWidget = new Intent(this, AppWidgetTrackAsset.class);
            updateWidget.setAction("update_widget");
            PendingIntent pending = PendingIntent.getBroadcast(this, 0, updateWidget, PendingIntent.FLAG_CANCEL_CURRENT);
            pending.send();
        } catch (PendingIntent.CanceledException e) {
            Log.e(Constants.UI_LOG,"Error widgetTrial()="+e.toString());
        }
    }
}


Макет виджета приложения выглядит следующим образом

app_widget_track_asset.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/widgetbackground"
    android:padding="@dimen/widget_margin">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/sosButton"
        android:text="SOS"
        android:textSize="20sp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        />
</RelativeLayout>

Ниже приведена основная часть файла манифеста для виджета

<receiver android:name=".appwidget.AppWidgetTrackAsset">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/app_widget_track_asset_info" />
        </receiver>
person Sreehari    schedule 15.03.2016