Aktualisierung von ListView in AppWidget funktioniert nicht

  • Antworten:2
Mike Hopeman
  • Forum-Beiträge: 31

21.01.2014, 22:17:00 via Website

Hallo zusammen,

ich möchte in einem Widget meinem RSS Feed anzeigen lassen. Ich kann den Feed auslesen und bereits Titel und Datum in die ListView im Widget einfügen. So weit so gut.

Leider funktioniert das Update bzw. Refresh nicht. Weder nach der eingestelleten UpdatePeriod noch nach einem Klick auf einen Button.

Hier mein Code, hoffe ihr könnt mir helfen:

WidgetProvider

[code]public class WidgetProvider extends AppWidgetProvider {

//Tag for Logging
private static final String TAG = "Widget";

// String to be sent on Broadcast as soon as Data is Fetched
// should be included on WidgetProvider manifest intent action
// to be recognized by this WidgetProvider to receive broadcast
public static final String DATA_FETCHED = "mypackage.DATA_FETCHED";
public static final String EXTRA_LIST_VIEW_ROW_NUMBER = "mypackage.EXTRA_LIST_VIEW_ROW_NUMBER";
public static final String WIDGET_BUTTON = "mypackage.WIDGET_BUTTON";

/*
* this method is called every 30 mins (30 min =30x60x1000) as specified on widgetinfo.xml this
* method is also called on every phone reboot from this method nothing is
* updated right now but instead RetmoteFetchService class is called this
* service will fetch data,and send broadcast to WidgetProvider this
* broadcast will be received by WidgetProvider onReceive which in turn
* updates the widget
*/
@Override
public void onUpdate(Context ctxt, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {

Log.d(TAG, "Hello WidgetProvider onUpdate");

/*
* int[] appWidgetIds holds ids of multiple instance of your widget
* meaning you are placing more than one widgets on your homescreen
*/

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

// Create an Intent for on click refresh
Intent intent = new Intent(WIDGET_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctxt, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);

// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews remoteViews = new RemoteViews(ctxt.getPackageName(),
R.layout.widget_layout);
remoteViews.setOnClickPendingIntent(R.id.refresh_widget,
pendingIntent);

// Tell the AppWidgetManager to perform an update on the current app widget
appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);

//start RemoteFetchService to parse XML in AsyncTask
Intent serviceIntent = new Intent(ctxt, RemoteFetchService.class);
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetIds[i]);
ctxt.startService(serviceIntent);
}
super.onUpdate(ctxt, appWidgetManager, appWidgetIds);
}

protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}

/*
* It receives the broadcast as per the action set on intent filters on
* Manifest.xml once data is fetched from RemoteFetchService,it sends
* broadcast and WidgetProvider notifies to change the data the data change
* right now happens on ListProvider as it takes RemoteFetchService
* listItemList as data
*/
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);

Log.d(TAG, "Hello WidgetProvider onReceive");

// if RSS Feed was parsed in RemoteFetchService.java
if (intent.getAction().equals(DATA_FETCHED)) {

Log.d(TAG, "Data fetched in Widget Provider in OnReceive");

int appWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);

// which layout to show on widget
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_layout);

// RemoteViews Service needed to provide adapter for ListView
Intent svcIntent = new Intent(context, WidgetService.class);
// passing app widget id to that RemoteViews Service
svcIntent
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
// setting a unique Uri to the intent
svcIntent.setData(Uri.parse(svcIntent
.toUri(Intent.URI_INTENT_SCHEME)));
// setting adapter to listview of the widget
remoteViews.setRemoteAdapter(appWidgetId, R.id.listViewWidget,
svcIntent);
// setting an empty view in case of no data
remoteViews.setEmptyView(R.id.listViewWidget, R.id.empty_view);

// onclick item listview

// This section makes it possible for items to have individualized
// behavior.
// It does this by setting up a pending intent template. Individuals
// items of a collection
// cannot set up their own pending intents. Instead, the collection
// as a whole sets
// up a pending intent template, and the individual items set a
// fillInIntent
// to create unique behavior on an item-by-item basis.
Intent toastIntent = new Intent(context, WidgetProvider.class);
// Set the action for the intent.
// When the user touches a particular view, it will have the effect
// of
// broadcasting TOAST_ACTION.
toastIntent.setAction(WidgetProvider.EXTRA_LIST_VIEW_ROW_NUMBER);
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent toastPendingIntent = PendingIntent.getBroadcast(
context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setPendingIntentTemplate(R.id.listViewWidget,
toastPendingIntent);

appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.listViewWidget);
}
// if item on list was clicked
if (intent.getAction().equals(EXTRA_LIST_VIEW_ROW_NUMBER)) {

Log.d(TAG, "List Item Clicked in OnReceive in Widget Provider");

int appWidgetId = intent.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);

AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);

// get position on listview which was clicked
int position = intent.getIntExtra(EXTRA_LIST_VIEW_ROW_NUMBER, 0);

// get RSSFeed
RSSFeed feed = ListItem.Feed;

Intent toastIntent = new Intent(context, ItemDetailActivity.class);
toastIntent.setAction(WidgetProvider.EXTRA_LIST_VIEW_ROW_NUMBER);
toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
appWidgetId);

/*
* Toast.makeText(context, "Clicked on position :" + viewIndex,
* Toast.LENGTH_SHORT).show();
*/

// start ItemDetailActivity
Intent detailIntent = new Intent(context, ItemDetailActivity.class);
detailIntent.putExtra("pos", position);
detailIntent.putExtra("feed", feed);
detailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(detailIntent);

}
// if refresh button was pressed
if (WIDGET_BUTTON.equals(intent.getAction())) {

Log.d(TAG,
"Refresh Button Clicked in OnReceive in Widget Provider");

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

} else {
Log.d(TAG, "No Data fetched in Widget Provider");
}

}
}[/code]

RemoteFetchService

1public class RemoteFetchService extends Service {
2
3 //Tag for Logging
4 private static final String TAG = "Widget";
5
6 private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
7 RSSFeed feed;
8 public Date pDate;
9 public static ArrayList<ListItem> listItemList;
10
11 @Override
12 public IBinder onBind(Intent arg0) {
13 return null;
14 }
15
16 /*
17 * Retrieve appwidget id from intent it is needed to update widget later
18 * start Async Task
19 */
20 @Override
21 public int onStartCommand(Intent intent, int flags, int startId) {
22
23 Log.d(TAG, "Hello RemoteFetchService onStartCommand");
24
25 if (intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID))
26 appWidgetId = intent.getIntExtra(
27 AppWidgetManager.EXTRA_APPWIDGET_ID,
28 AppWidgetManager.INVALID_APPWIDGET_ID);
29
30 // fetchDataFromWeb();
31 new AsyncLoadXMLFeed().execute();
32
33 return super.onStartCommand(intent, flags, startId);
34 }
35
36 /**
37 * AsyncTask which parses the xml and post it
38 **/
39 public class AsyncLoadXMLFeed extends AsyncTask<Void, Void, RSSFeed> {
40
41
42 protected RSSFeed doInBackground(Void... params) {
43 try {
44
45 Log.d(TAG, "Starte Parsing von URL in Widget");
46 // Obtain feed
47 DOMParser myParser = new DOMParser();
48 feed = myParser.parseXml("http://www.test.de/feed");
49 Log.d(TAG,
50 "ItemCount Parser in Widget: " + feed.getItemCount());
51
52 return feed;
53
54 } catch (Exception e) {
55 Log.d(TAG, "Exception beim Parsen: " + e.toString());
56 return null;
57 }
58 }
59
60 @Override
61 protected void onPostExecute(RSSFeed parsed_feed) {
62 // super.onPostExecute(result);
63 Log.d(TAG, "Async Parse fertig");
64 listItemList = new ArrayList<ListItem>();
65
66 if (feed != null) {
67 try {
68 int length = feed.getItemCount();
69 for (int i = 0; i < length; i++) {
70
71 String date = calc_date_difference(feed, i);
72
73 final ListItem listItem = new ListItem();
74 ListItem.Feed = feed;
75 listItem.heading = feed.getItem(i).getTitle();
76 listItem.pubDate = date;
77 Log.d(TAG+" Heading", feed.getItem(i).getTitle());
78 Log.d(TAG+" PubDate", date);
79 listItemList.add(listItem);
80 }
81
82 } catch (Exception e) {
83 Log.d(TAG, "Exception onPostExecute: " + e.toString());
84 }
85 } else {
86 Log.d(TAG, "Feed in onPostExecute ist null");
87 }
88
89 // start intent and broadcast WidgetProvider, that data is fetched
90 Intent widgetUpdateIntent = new Intent();
91 widgetUpdateIntent.setAction(WidgetProvider.DATA_FETCHED);
92 widgetUpdateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
93 appWidgetId);
94 sendBroadcast(widgetUpdateIntent);
95
96 // stop service
97 stopSelf();
98 }
99 }
100
101 /**
102 * Method to calculate the time difference
103 **/
104 public String calc_date_difference(RSSFeed feed, int pos) {
105 // calculate the time difference to the actual system time
106 String pubDate = feed.getItem(pos).getDate();
107 SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z",
108 Locale.ENGLISH);
109 try {
110 try {
111 pDate = df.parse(pubDate);
112 } catch (java.text.ParseException e) {
113 e.printStackTrace();
114 }
115 pubDate = "Vor " + DateUtils.getDateDifference(pDate);
116 return pubDate;
117 } catch (ParseException e) {
118 Log.e(TAG, "Error parsing date..");
119 return null;
120 }
121 }
122}

ListProvider

1/**
2 * If you are familiar with Adapter of ListView,this is the same as adapter with
3 * few changes
4 *
5 */
6public class ListProvider implements RemoteViewsFactory {
7
8 // Tag for Logging
9 private static final String TAG = "Widget";
10
11 private RemoteViews views;
12 private Context ctxt = null;
13 private int appWidgetId;
14 private ArrayList<ListItem> listItemList = new ArrayList<ListItem>();
15 public ImageLoader imageLoader;
16 private Bitmap bmp;
17
18 public ListProvider(Context ctxt, Intent intent) {
19 this.ctxt = ctxt;
20 appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
21 AppWidgetManager.INVALID_APPWIDGET_ID);
22
23 if (RemoteFetchService.listItemList != null)
24 listItemList = (ArrayList<ListItem>) RemoteFetchService.listItemList
25 .clone();
26 else
27 listItemList = new ArrayList<ListItem>();
28 }
29
30 @Override
31 public void onCreate() {
32 // no-op
33 Log.d(TAG, "Hello ListProvider onCreate");
34 }
35
36 @Override
37 public void onDestroy() {
38 // no-op
39 }
40
41 @Override
42 public int getCount() {
43 return listItemList.size();
44 }
45
46 /*
47 * Similar to getView of Adapter where instead of Viewwe return RemoteViews
48 */
49 @Override
50 public RemoteViews getViewAt(int position) {
51
52 final RemoteViews remoteView = new RemoteViews(ctxt.getPackageName(),
53 R.layout.widget_row);
54 ListItem listItem = listItemList.get(position);
55 remoteView.setTextViewText(R.id.heading, listItem.heading);
56 remoteView.setTextViewText(R.id.pubDate, listItem.pubDate);
57
58 // onclick item listview
59 Intent fillInIntent = new Intent();
60 fillInIntent.putExtra(WidgetProvider.EXTRA_LIST_VIEW_ROW_NUMBER,
61 position);
62 remoteView.setOnClickFillInIntent(R.id.heading, fillInIntent);
63
64 return remoteView;
65 }
66
67 @Override
68 public RemoteViews getLoadingView() {
69 return (null);
70 }
71
72 @Override
73 public int getViewTypeCount() {
74 return (1);
75 }
76
77 @Override
78 public long getItemId(int position) {
79 return (position);
80 }
81
82 @Override
83 public boolean hasStableIds() {
84 return (true);
85 }
86
87 @Override
88 public void onDataSetChanged() {
89 // This code is executed if the refresh button is pressed or after the
90 // update period
91
92 // start RemoteFetchService to parse XML in AsyncTask
93 Intent serviceIntent = new Intent(ctxt, RemoteFetchService.class);
94 serviceIntent
95 .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
96 ctxt.startService(serviceIntent);
97 }
98}

feed_list.xml

1<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
2 android:minHeight="200dp"
3 android:minWidth="200dp"
4 android:updatePeriodMillis="30000"
5 android:initialLayout="@layout/widget_layout"
6 android:autoAdvanceViewId="@+id/words"
7 android:previewImage="@drawable/widget_preview"
8 android:resizeMode="vertical|horizontal"
9/>

Antworten
impjor
  • Forum-Beiträge: 1.793

22.01.2014, 16:29:25 via App

Bitte debugge zuerst selber und poste hier die Ergebnisse:
1. Wird die refresh()-Methode aufgerufen?
2. Downloaded sie den korrekten Code?

Dann beschreibe doch bitte dein Problem genauer:
Was passiert statt dem erwarteten refresh?
Wo in dem kompletten Code ist die entsprechende Methode?
So viel Code will sich keiner komplett durchlesen!

LG

Liebe Grüße impjor.

Für ein gutes Miteinander: Unsere Regeln
Apps für jeden Einsatzzweck
Stellt eure App vor!

Antworten
Mike Hopeman
  • Forum-Beiträge: 31

22.01.2014, 20:27:19 via Website

Hallo,

ich versuche das Problem genauer zu beschreiben:

Ich platziere das Widget auf dem Homescreen und es erscheint auch die Liste mit dem gewünschten Inhalt => Inhalt wird korrekt heruntergeladen und die onUpdate() Funktion des Widgets wird aufgerufen!

Ich denke mein Hauptproblem liegt in den Aufrufen von [code]appWidgetManager.updateAppWidget(appWidgetIds[i], remoteViews);[/code] und [code]appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds[i], R.id.listViewWidget);[/code]. Ich verstehe es so:

1. In meiner onUpdate() Methode meines AppWidgetProvider rufe ich notifyAppWidgetViewDataChanged auf und dadurch wird automatisch die Funktion onDataSetChanged() aufgerufen ( wie hier beschrieben wird).

2. Dort rufe ich via Intent meinen Service auf und parse dort den Feed.

3. Via Intent gehe ich zurück in die onReceive() Methode des WidgetProvider und rufe meine RemoteViewsFactory auf wo die ListView befüllt wird und mit updateAppWidget(); das Widget geupdatet wird.

Ist das so richtig? Momentan habe ich auch das Problem, dass ein Klick auf meinen Refresh Button nicht mehr erkannt wird, obwohl der doch richtig im Code implementiert ist, oder?

— geändert am 22.01.2014, 20:27:51

Antworten