ListView в виджетах добавляет случайные элементы для прокрутки и изменения размера (вложенные удаленные просмотры)

Обновление: я создал репозиторий с меньшим количеством кода, чтобы его было немного легче понять.

Я пытаюсь создать виджет. Я сделал это, как описано здесь: https://stackoverflow.com/a/6093753/2180161

Он работает частично, но у меня действительно странная ошибка. Я сделал скринкаст , так что легче понять, что я имею в виду: http://c.maysi.de/c6H9

Скриншот : Введите описание изображения здесь

Как вы видите, есть некоторые элементы, которые были добавлены случайным образом. ( RemoteViews которые были добавлены в другой объект RemoteViews ) То же самое происходит при изменении размера виджета.

То, что я распечатал в журнале, похоже на ожидаемый. Нет неправильных данных. При прокрутке также нет новых записей в журнале.

Это мой код:

RemoteViewsFactory:

 @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class MyWidgetViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static ArrayList<Item> items = new ArrayList<>(); private static int itemnr = 0; private static int subitemnr = 0; private int appWidgetId; private Context context; public MyWidgetViewsFactory(Context context, Intent intent) { this.context = context; appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); //Some random data to display for (int i = 0; i < 10; i++) { Item item = new Item(String.valueOf(itemnr++)); for (int j = 0; j < 3; j++) { String[] subitem = {String.valueOf(subitemnr++), String.valueOf(subitemnr++), String.valueOf(subitemnr++)}; item.addSubitem(subitem); } items.add(item); } } @Override public void onCreate() { // no-op } @Override public void onDestroy() { // no-op } @Override public int getCount() { return items.size(); } @Override public RemoteViews getViewAt(int position) { Log.d("MyWidgetViewsFactory", "getViewAt(" + position + "):" + items.get(position)); Item item = items.get(position); RemoteViews itemView = new RemoteViews(context.getPackageName(), R.layout.widget_listview_item); itemView.setTextViewText(R.id.textView_itemnr, item.getItemNr()); for (String[] s : item.getSubitems()) { Log.d("MyWidgetViewsFactory", "subitem:" + s[0] + "|" + s[1] + "|" + s[2]); RemoteViews subitem = new RemoteViews(context.getPackageName(), R.layout.widget_listview_subitem); subitem.setTextViewText(R.id.textView_1, s[0]); subitem.setTextViewText(R.id.textView_2, s[1]); subitem.setTextViewText(R.id.textView_3, s[2]); itemView.addView(R.id.linearLayout_item_body, subitem); } return itemView; } @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); } @Override public void onDataSetChanged() { // no-op } class Item { private ArrayList<String[]> subitems = new ArrayList<>(); private String itemnr = ""; Item(String itemnr) { this.itemnr = itemnr; } Item() { } public void addSubitem(String[] subitem) { this.subitems.add(subitem); } public ArrayList<String[]> getSubitems() { return subitems; } public String getItemNr() { return itemnr; } public void setItemNr(String itemnr) { this.itemnr = itemnr; } } } 

AppWidgetProvider

  public class MyWidgetProvider extends AppWidgetProvider { @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // There may be multiple widgets active, so update all of them Log.d("MyWidgetProvider", "appWidgetIds.lenght:" + appWidgetIds.length); for (int appWidgetId : appWidgetIds) { Intent svcIntent = new Intent(context, MyWidgetService.class); svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.widget_root); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) widget.setRemoteAdapter(R.id.listView_widget, svcIntent); else widget.setRemoteAdapter(appWidgetId, R.id.listView_widget, svcIntent); /* Intent clickIntent = new Intent(context, MainActivity.class); PendingIntent clickPI = PendingIntent.getActivity(context, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT); widget.setPendingIntentTemplate(R.id.listView_widget, clickPI);*/ appWidgetManager.updateAppWidget(appWidgetId, widget); } } @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 } } 

RemoteViewsService

 @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class MyWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return (new MyWidgetViewsFactory(this.getApplicationContext(), intent)); } } 

Все другие ресурсы можно найти в репо в GitHub.


Выход Logcat:

 08-08 02:11:10.858 32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(0):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@3e7179c9 08-08 02:11:10.860 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:0|1|2 08-08 02:11:10.864 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:3|4|5 08-08 02:11:10.866 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:6|7|8 08-08 02:11:10.927 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(0):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@3e7179c9 08-08 02:11:10.927 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:0|1|2 08-08 02:11:10.927 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:3|4|5 08-08 02:11:10.927 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:6|7|8 08-08 02:11:10.931 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(1):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@23e248ce 08-08 02:11:10.931 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:9|10|11 08-08 02:11:10.931 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:12|13|14 08-08 02:11:10.931 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:15|16|17 08-08 02:11:10.933 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(2):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@16dbf3ef 08-08 02:11:10.933 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:18|19|20 08-08 02:11:10.933 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:21|22|23 08-08 02:11:10.933 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:24|25|26 08-08 02:11:10.936 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(3):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@19d3defc 08-08 02:11:10.936 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:27|28|29 08-08 02:11:10.936 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:30|31|32 08-08 02:11:10.936 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:33|34|35 08-08 02:11:10.938 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(4):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@ee985 08-08 02:11:10.938 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:36|37|38 08-08 02:11:10.938 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:39|40|41 08-08 02:11:10.938 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:42|43|44 08-08 02:11:10.941 32427-32443/? D/MyWidgetViewsFactory﹕ getViewAt(8):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@335e23da 08-08 02:11:10.941 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:72|73|74 08-08 02:11:10.941 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:75|76|77 08-08 02:11:10.941 32427-32443/? D/MyWidgetViewsFactory﹕ subitem:78|79|80 08-08 02:11:10.943 32427-32447/? D/MyWidgetViewsFactory﹕ getViewAt(9):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@229de00b 08-08 02:11:10.943 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:81|82|83 08-08 02:11:10.943 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:84|85|86 08-08 02:11:10.943 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:87|88|89 08-08 02:11:10.945 32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(5):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@2afdeee8 08-08 02:11:10.945 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:45|46|47 08-08 02:11:10.945 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:48|49|50 08-08 02:11:10.945 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:51|52|53 08-08 02:11:10.948 32427-32444/? D/MyWidgetViewsFactory﹕ getViewAt(7):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@1c599901 08-08 02:11:10.948 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:63|64|65 08-08 02:11:10.948 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:66|67|68 08-08 02:11:10.948 32427-32444/? D/MyWidgetViewsFactory﹕ subitem:69|70|71 08-08 02:11:10.951 32427-32447/? D/MyWidgetViewsFactory﹕ getViewAt(6):de.mayerhofersimon.listviewproblem.MyWidgetViewsFactory$Item@368aa3a6 08-08 02:11:10.951 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:54|55|56 08-08 02:11:10.951 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:57|58|59 08-08 02:11:10.951 32427-32447/? D/MyWidgetViewsFactory﹕ subitem:60|61|62 

Поэтому данные передаются правильно. Он просто не отображается правильно …

BTW: вот что должно выглядеть: http://c.maysi.de/cB8K

Может быть, проблема связана с вложенными удаленными просмотрами? Потому что все внешние удаленные просмотры отображаются правильно …

Solutions Collecting From Web of "ListView в виджетах добавляет случайные элементы для прокрутки и изменения размера (вложенные удаленные просмотры)"

Ваша мысль о повторной переработке ListView правильная. Вам нужно понять, как все работает на уровне земли.

Шаблон MVC

Графика Android работает с шаблоном MVC, т.е. с образцом Model-View и Controller. Модель – это ваши данные, база данных в вашем случае. Вид – это ваш макет или графическая часть, например ListView или RecyclerView или RemoteView. Контроллер изменяет ваше представление после обновления данных, родительского представления или ViewGroup в вашем случае RemoteViewsService.RemoteViewsFactory является контроллером. Я предлагаю читать дальше, перейдя по модели MVC.

Как реализуется шаблон?

Каждый раз, когда данные изменяются, просмотр должен обновляться контроллером. Структура Android дает вам возможность отображать ваше представление в данной позиции с вашими данными, переопределяя getViewAt(int position) . Контроллер вызывает getViewAt(int position) чтобы получить представление в данной позиции в ListView или RecyclerView. ListView или RecyclerView отображает на экране только видимые строки. Например, если у вас есть 100 элементов в ListView, и на экране отображается только 7, это вызовет getItemAt (int) 7 раз. Каждый раз, когда вы прокручиваете, getItemAt (int) вызывается для видимых строк. В ListView и RemoteView есть свобода для повторного использования / повторного использования ранее переданного представления, возвращаемого getItemAt (int position). Это гарантирует, что память, потребляемая графической частью вашего приложения, ограничена

Почему странное поведение?

Прежде всего, каждая видимая вещь на экране – это View такой как TextView, ImageView и ListView и т. Д. Если он не может быть отображен на экране. RemoteView – это не вид. Вы передаете макет и данные, которые будут отображаться с помощью RemoteView (View + Data).

Здесь я имею в виду ваш Screencast для объяснения.

1) Инициализация : ListView в вашем случае сначала создает 6 строк на основе видимого пространства на экране, а getViewAt(int position) вызывается один раз, если getCount() возвращает 1. Я прошу проверить возвращаемое значение getCount () Адаптера списка.
2) Вы прокручиваете вниз : ничего не происходит с ListView и рендерингом новых строк.
3) Вы прокрутили вверх : getPositionAt (int position) вызывается снова и RemoteView передается обратно. Теперь две строки видны. Я прошу проверить возвращаемое значение getCount (). Это должно быть 2, если не причина, которая может быть кешированием строк ListView.
4) Вы прокрутили вниз : ничего не происходит с ListView и рендерингом новых строк.
5) Вы прокрутили : см. 3. getCount () должен быть 3 и так далее.

Что ты должен делать?

В соответствии с вашей реализацией вы создали RemoteView только один раз и попытались повторно использовать один и тот же вид в getItemAt(int) , возможно, нужно сохранить время макетирования макета.
Чтобы исправить эту проблему, вы ДОЛЖНЫ предоставить FRESH RemoteView каждый раз, когда getItemAt(int) .

 @Override public RemoteViews getViewAt(int position) { Log.d("VplanWidgetViewsFactory", "getViewAt("+position+"):"+stunden.get(position)); //TODO: Store context when constructor is called. RemoteView rv = new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget); rv.setTextViewText(R.id.textView_lesson_nr, "" + (position + 1) + "."); <= I have not tested this. return rv; //return stunden.get(position); <=COMMENT THIS } 

Я сам нашел ответ.

Чтобы устранить проблему при странном добавлении просмотров при прокрутке и изменении размера, вы должны вызвать removeAllViews в макете, где были добавлены подзаголовки:

 @Override public RemoteViews getViewAt(int position) { ... RemoteViews itemView = new RemoteViews(context.getPackageName(), R.layout.widget_listview_item); itemView.removeAllViews(R.id.linearLayout_item_body); ... return itemView; } 

И проблема в том, что представления не отображаются, из-за цвета: после добавления

  subitem.setTextColor(R.id.textView_1, context.getResources().getColor(R.color.abc_primary_text_material_light)); subitem.setTextColor(R.id.textView_2, context.getResources().getColor(R.color.abc_primary_text_material_light)); subitem.setTextColor(R.id.textView_3, context.getResources().getColor(R.color.abc_primary_text_material_light)); 

Отображаются все виды:

Введите описание изображения здесь

Ваша проблема, кажется, в:

 if(stundenContainer[j]!=null) Log.d("VplanWidgetViewsFactory", "stundenContainer["+j+"]:" + stundenContainer[j].toString()); else Log.d("VplanWidgetViewsFactory", "stundenContainer[" + j + "]:null"); if (stundenContainer[j] == null) { //Freistunde Log.d("VplanWidgetViewsFactory", "Freistunde"); // HERE ----- stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget)); faecher.add(new RemoteViews(context.getPackageName(), R.layout.fragment_fach)); stunden.get(stunden.size() - 1).setTextViewText(R.id.textView_lesson_nr, "" + (j + 1) + "."); } else if (!stundenContainer[j].get(0).getSubject().equals("ignore")) { Log.d("VplanWidgetViewsFactory", "stundenContainer[j].get(0).getSubject(): " + stundenContainer[j].get(0).getSubject()); // HERE ----- stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget)); 

Вы добавляете его дважды .., но только когда первый элемент не игнорируется, поэтому он кажется случайным.

 stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget));