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

Android Widget with collection (ListView) not displaying items

我正在尝试为我的应用程序做一个简单的小部件,它使用 ListView 在小部件上显示来自 API 的数据,但列表是空的,即使日志显示数据已成功下载。 显示空白 listView,其中没有数据。没有显示错误。这是什么原因?

我在尝试解决这个问题时使用的资源:

官方文档 - https://developer.android.com/guide/topics/appwidgets#collections

类似问题 - How to use listview in a widget?

我的 AppWidgetProvider

 public class Widget extends AppWidgetProvider {
    RemoteViews views;

    void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                         int appWidgetId) {

        CharSequence widgetText = context.getString(R.string.appwidget_text);
        // Construct the RemoteViews object
        views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @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) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
            Intent intent = new Intent(context, WidgetService.class);
            remoteViews.setRemoteAdapter(R.id.appwidget_list_view, intent);
            appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
        }
    }

WidgetFactory 和 WidgetService


public class WidgetFactory implements RemoteViewsService.RemoteViewsFactory{
    final Context context;
    List<SimpleCoin> list;
    List<Coin> coinList;

    public WidgetFactory(Context context, Intent intent) {
        this.context = context;
    }

    @Override
    public void onCreate() {
        //get data from database
        CoinDatabase coinDatabase = CoinDatabase.buildDatabase(context);
        list = coinDatabase.coinDao().getAllSimpleCoinsNonLive();
        coinList = new ArrayList<>();
    }

    @Override
    public void onDataSetChanged() {
        //based on data from database download content (JSON)
        if(list != null) {
            Log.d("Widget", "Coin size -> " + list.size());
            StringBuilder id = new StringBuilder("id=" + list.get(0).getId());
            for (int j = 0; j < list.size(); j++) {
                id.append(",");
                id.append(list.get(j).getId());
            }
            String finalUrl = URL + id.toString() + API_KEY;
            Observable.fromCallable(() -> {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                java.net.URL url1 = new URL(finalUrl);
                connection = (HttpURLConnection) url1.openConnection();
                connection.connect();
                InputStream stream = connection.getInputStream();
                reader = new BufferedReader(new InputStreamReader(stream));
                StringBuilder buffer = new StringBuilder();
                String line = "";
                while ((line = reader.readLine()) != null) {
                    buffer.append(line).append("\n");
                }
                connection.disconnect();
                reader.close();
                return buffer.toString();
            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {
                }
                @Override
                public void onNext(@NonNull String s) {
                    if (s != null) {
                        try {
                            //Create new coin and update list of coins
                            JSONObject object = new JSONObject(s);
                            for (int i = 0; i < list.size(); i++) {
                                JSONObject jsonObject = object.getJSONObject("data").getJSONObject(String.valueOf(list.get(i).getId()));
                                Log.d("Widget", "Coin -> " + jsonObject.getString("name") +  jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price") + jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"));
                                Coin coin = new Coin(jsonObject.getString("name"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"), false);
                                coinList.add(coin);
                                getViewAt(i);
                            }

                        } catch (JSONException e) {
                            Log.d("Widget", "Coin -> " + e.toString());
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(@NonNull Throwable e) {
                }

                @Override
                public void onComplete() {
                }
            });
        }
    }

    @Override
    public void onDestroy() {}

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public RemoteViews getViewAt(int i) {
        //Set ListData based on coinList
        Log.d("Widget", "Coin size getViewAt -> " + list.size() + "item num ->" + i);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget_list_item);
        if(coinList !=  null)
        if(coinList.size() > i) {
            Log.d("Widget", "Coin position added" + coinList.get(i).getName());
            remoteViews.setTextViewText(R.id.widgetItemTaskNameLabel, coinList.get(i).getName());
        }
        return remoteViews;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public int getViewTypeCount() {
        return 1;
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

public class WidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WidgetFactory(this.getApplicationContext(), intent);
    }
}

这是我的日志:


D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin -> testCoin0.00.0

D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin position added testCoin

my_widget.xml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@color/colorPrimary"
    android:padding="@dimen/widget_margin">
    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_margin="8dp"
        android:background="@color/colorPrimary"
        android:contentDescription="@string/appwidget_text"
        android:text="@string/appwidget_text"
        android:textColor="#ffffff"
        android:textSize="24sp"
        android:textStyle="bold|italic" />
    <ListView
        android:id="@+id/appwidget_list_view"
        android:layout_width="match_parent"
        android:layout_margin="8dp"
        tools:listitem="@layout/my_widget_list_item"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:layout_below="@id/appwidget_text">
    </ListView>
</RelativeLayout>

my_widget_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:paddingLeft="@dimen/widget_listview_padding_x"
    android:paddingRight="@dimen/widget_listview_padding_x"
    android:paddingStart="@dimen/widget_listview_padding_x"
    android:paddingEnd="@dimen/widget_listview_padding_x"
    android:minHeight="@dimen/widget_listview_item_height"
    android:weightSum="2"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/widgetItemTaskNameLabel"
        android:layout_width="0dp"
        android:gravity="start"
        android:layout_weight="1"
        android:textColor="@color/colorPrimary"
        android:layout_gravity="center_vertical"
        android:layout_height="wrap_content" />

</LinearLayout>

我发现了我的错误,原来我的 API 调用不应该是异步的,因为正如文档所说:

public void onDataSetChanged() -> You can do heaving lifting in here, synchronously. For example, if you need to process an image, fetch something from the network, etc., it is ok to do it here, synchronously. The widget will remain in its current state while work is being done here, so you don't need to worry about locking up the widget.

所以从异步调用中获取我的数据是数据未显示的原因