I'm trying to build a widget for my Android app and I'm running into the problem that while the ListView does find data (through the debugger I can see the getViewAt method in my RemoteViewFactory implementation is called and returning a proper RemoteViews), each entry found is simply shown in my widget as "Loading..."
For the record: there are indeed 4 entries found in my database and the getViewAt method is called four times, each time returning a proper list item.
I've based my code off the Android documentation, having used the StackView sample as technical reference.
In trying to fix this problem I've consulted stackoverflow and found some past questions. The most common suggestions for fixes are:
- Ensure the getViewTypeCount method returns a value other than 0 (usually 1, as most widgets will only use one view type);
- Ensure only supported views are used;
- A few more vague suggestions as some arbitrary view properties should or shouldn't be used.
I've tried all of the suggestions above and am using only supported views yet still my items aren't displayed. Does someone here have an idea?
A list of relevant code I've written:
Entries added to my AndroidManifest.xml file
<service
android:name=".widget.DfdWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver android:name=".widget.DfdWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/dfd_widget_info" />
</receiver>
Widget layout xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80DDE8ED"
android:padding="@dimen/widget_margin">
<ListView
android:id="@+id/widgetRemindersListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/dfd_widget_list_item">
</ListView>
<TextView
android:id="@+id/widgetEmptyViewText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="You have no reminders for today, nice!"
android:textAlignment="center"
android:textSize="20sp" />
</RelativeLayout>
Widget list item xml
(As a test I did try removing everything except the TextViews to see if Android didn't find the ImageViews offending. It didn't matter.)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/widgetListItem"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/widgetCompletedCheckBox"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentStart="true"
android:layout_marginStart="10dp"
android:layout_marginTop="15dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
app:srcCompat="@android:drawable/checkbox_off_background" />
<TextView
android:id="@+id/widgetReminderName"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_toEndOf="@id/widgetCompletedCheckBox"
android:text="Reminder name"
android:textColor="#000000"
android:textSize="24sp" />
<TextView
android:id="@+id/widgetReminderDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/widgetReminderName"
android:layout_toEndOf="@id/widgetCompletedCheckBox"
android:text="Ma 1 jan"
android:textColor="#2196F3" />
<ImageView
android:id="@+id/widgetReminderRepeating"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_below="@id/widgetReminderName"
android:layout_toEndOf="@id/widgetReminderDate"
android:background="#00FFFFFF"
app:srcCompat="@android:drawable/ic_menu_revert"
tools:visibility="invisible" />
<ImageView
android:id="@+id/widgetIsImportant"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentEnd="true"
app:srcCompat="@android:drawable/btn_star_big_off" />
</RelativeLayout>
Widget Provider class
public class DfdWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
Intent intent = new Intent(context, DfdWidgetService.class);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.dfd_widget);
remoteViews.setRemoteAdapter(R.id.widgetRemindersListView, intent);
remoteViews.setEmptyView(R.id.widgetRemindersListView, R.id.widgetEmptyViewText);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
@Override
public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
super.onRestored(context, oldWidgetIds, newWidgetIds);
}
}
Widget Service Class
public class DfdWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new DfdRemoteViewsFactory(getApplicationContext());
}
class DfdRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private final Context context;
private ReminderRepository reminderRepository;
private List<Reminder> reminders;
public DfdRemoteViewsFactory(Context context) {
this.context = context;
this.reminderRepository = ReminderRepository.getReminderRepository(context);
}
@Override
public void onCreate() {
reminders = reminderRepository.getAllRemindersForTodayList();
}
@Override
public void onDataSetChanged() {
reminders = reminderRepository.getAllRemindersForTodayList();
}
@Override
public void onDestroy() {
reminders.clear();
}
@Override
public int getCount() {
return reminders.size();
}
@Override
public RemoteViews getViewAt(int position) {
Reminder reminder = reminders.get(position);
RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.id.widgetListItem);
remoteView.setTextViewText(R.id.widgetReminderName, reminder.getName());
// Removed code that sets the other fields as I tried it with and without and it didn't matter. So removed for brevity
return remoteView;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int position) {
return reminders.get(position).getId();
}
@Override
public boolean hasStableIds() {
return true;
}
}
}
RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.id.widgetListItem);
– IngetViewAt()
, that should be anR.layout
, not anR.id
. Probably just a typo, assuming you named the layout the same thing. Also, not relevant to the current issue, but anything that uses theapp
namespace prefix in your layouts isn't going to work inRemoteViews
; e.g.,app:srcCompat
. Just FYI. – Holozoic