带有 ListView 的小部件为找到的每个项目显示“正在加载”

Posted

技术标签:

【中文标题】带有 ListView 的小部件为找到的每个项目显示“正在加载”【英文标题】:Widget with ListView shows 'Loading' for each item found 【发布时间】:2020-08-18 02:37:11 【问题描述】:

我正在尝试为我的 android 应用程序构建一个小部件,但我遇到了一个问题,即 ListView 确实 查找数据(通过调试器我可以在我的 RemoteViewFactory 中看到 getViewAt 方法调用实现并返回正确的 RemoteViews),找到的每个条目在我的小部件中简单地显示为“正在加载...”

记录一下:在我的数据库中确实找到了 4 个条目,并且 getViewAt 方法被调用了四次,每次都返回一个正确的列表项。

我的代码基于Android documentation,并使用StackView sample 作为技术参考。

在尝试解决此问题时,我咨询了 *** 并找到了一些 past questions。最常见的修复建议是:

确保 getViewTypeCount 方法返回 0 以外的值(通常为 1,因为大多数小部件只会使用一种视图类型); 确保只使用supported views; 一些更模糊的建议,因为一些任意的视图属性应该或不应该使用。

我已经尝试了上述所有建议,并且只使用受支持的视图,但我的项目仍然没有显示。这里有人有想法吗?

我写的相关代码列表:

条目已添加到我的 AndroidManifest.xml 文件中

    <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>

小部件布局 xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:background="#80DDE8ED"
android:padding="@dimen/widget_margin">

<ListView
    android:id="@+id/widgetRemindersListView"
    android:layout_
    android:layout_
    tools:listitem="@layout/dfd_widget_list_item">

</ListView>

<TextView
    android:id="@+id/widgetEmptyViewText"
    android:layout_
    android:layout_
    android:text="You have no reminders for today, nice!"
    android:textAlignment="center"
    android:textSize="20sp" />

</RelativeLayout>

小部件列表项 xml

(作为测试,我确实尝试删除除 TextViews 之外的所有内容,以查看 Android 是否发现 ImageViews 有问题。没关系。)

<?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_
android:layout_>

<ImageView
    android:id="@+id/widgetCompletedCheckBox"
    android:layout_
    android:layout_
    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_
    android:layout_
    android:layout_toEndOf="@id/widgetCompletedCheckBox"
    android:text="Reminder name"
    android:textColor="#000000"
    android:textSize="24sp" />

<TextView
    android:id="@+id/widgetReminderDate"
    android:layout_
    android:layout_
    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_
    android:layout_
    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_
    android:layout_
    android:layout_alignParentEnd="true"
    app:srcCompat="@android:drawable/btn_star_big_off" />
</RelativeLayout>

Widget Provider 类

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);
    

小部件服务类

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); - 在getViewAt() 中,应该是R.layout,而不是R.id。可能只是一个错字,假设您将布局命名为相同的东西。此外,与当前问题无关,但在您的布局中使用 app 命名空间前缀的任何内容都无法在 RemoteViews 中工作;例如,app:srcCompat。仅供参考。 实际上,您的这条评论一针见血!你和 CommonsWare 都真的很快就找到了! 【参考方案1】:
RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.id.widgetListItem)

RemoteViews constructor that you are trying to use 将布局资源 ID 作为第二个参数。您正在传递一个小部件 ID。

因此,将R.id.widgetListItem 替换为R.layout.whateverYourLayoutNameIsForTheListItems,在此将whateverYourLayoutNameIsForTheListItems 替换为列表项的布局名称。

【讨论】:

我不知道你和 Mike M 怎么这么快就找到了,但是干杯! @Lynxian:正如您所指出的,正确计数的显示表明您的RemoteViewsService 正在被调用。对行布局 XML 的快速检查表明“正在加载...”文本不是来自您的代码,这意味着小部件主机在使用您的行时遇到了困难。这将问题限制在为行设置 RemoteViews 的位置,而这只是几行代码。

以上是关于带有 ListView 的小部件为找到的每个项目显示“正在加载”的主要内容,如果未能解决你的问题,请参考以下文章

带有集合(ListView)的Android小部件不显示项目

Widget + ListView + TaskStackBuilder -> BroadcastReceiver 不会被找到

listview.builder 不在屏幕上显示项目

解决不可接受的 ListView 小部件

如何将带有列表视图的小部件限制为其子项的大小?

具有多个挂起意图的ListView小部件