如何使用来自 Activity 的列表视图更新 App Widget
How to update App Widget with list view from an Activity
我知道这个问题已经被问过很多次了,但我从头到尾浏览了文档,阅读了这里的所有答案,其中 none 对他们有所帮助。
老实说,每个答案都说明了如何解决这个问题。
现在回到我的问题。我想更新一些 activity 的小部件列表视图,为此我创建了 WidgetProvider#sendUpdateBroadcastToAllWidgets()
,我从 activity 调用它。
它最终会调用 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;
}
}
我的项目中有一个小部件实现。我修改了以下代码,以便可以从我的应用程序中的 Activity 之一更改小部件中的数据。仅显示特定用例的基本代码。
我的小部件中有一个带有文本的按钮。通过登录按钮单击我的 Activity ,我正在修改我的小部件中的按钮文本
下面是我的AppWidgetProvider.java
class
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
}
}
下面是我的 Activity 我在点击登录按钮时更新小部件按钮文本的地方
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>
我会说 notifyAppWidgetViewDataChanged
方法应该适合你。
您必须构建 AppWidgetManager
并获得 appWidgetIds
,然后只需在 AppWidgetManager
上调用 notifyAppWidgetViewDataChanged
。
伪代码,
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetIds[] = appWidgetManager.getAppWidgetIds(
new ComponentName(context, WidgetProvider.class));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listview);
有关更多信息,您可以查看我的回答 here,其中包含关于 github 的演示。
我知道这个问题已经被问过很多次了,但我从头到尾浏览了文档,阅读了这里的所有答案,其中 none 对他们有所帮助。 老实说,每个答案都说明了如何解决这个问题。
现在回到我的问题。我想更新一些 activity 的小部件列表视图,为此我创建了 WidgetProvider#sendUpdateBroadcastToAllWidgets()
,我从 activity 调用它。
它最终会调用 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;
}
}
我的项目中有一个小部件实现。我修改了以下代码,以便可以从我的应用程序中的 Activity 之一更改小部件中的数据。仅显示特定用例的基本代码。
我的小部件中有一个带有文本的按钮。通过登录按钮单击我的 Activity ,我正在修改我的小部件中的按钮文本
下面是我的AppWidgetProvider.java
class
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
}
}
下面是我的 Activity 我在点击登录按钮时更新小部件按钮文本的地方
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>
我会说 notifyAppWidgetViewDataChanged
方法应该适合你。
您必须构建 AppWidgetManager
并获得 appWidgetIds
,然后只需在 AppWidgetManager
上调用 notifyAppWidgetViewDataChanged
。
伪代码,
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int appWidgetIds[] = appWidgetManager.getAppWidgetIds(
new ComponentName(context, WidgetProvider.class));
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listview);
有关更多信息,您可以查看我的回答 here,其中包含关于 github 的演示。