Как обновить виджет App с видом списка из Activity

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

Теперь вернемся к моему вопросу. Я хочу обновить вид списка виджетов из некоторой активности, и для этой цели я создал 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; } } 

Я бы сказал, что метод 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.

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

Здесь у меня есть кнопка с текстом в моем виджете. Через кнопку входа в систему щелкните в моей операции, я изменяю текст кнопки в своем виджете

Ниже мой класс 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>