ListView en el widget agrega aleatoriamente los elementos en el desplazamiento y el cambio de tamaño (vistas remotas anidadas)

Actualización: Creé un repositorio con menos código para hacerlo un poco más fácil de entender.

Estoy intentando crear un widget. Lo hice como se describe aquí: https://stackoverflow.com/a/6093753/2180161

Funciona parcialmente, pero tengo un error muy extraño. Hice un screencast , por lo que es más fácil entender lo que quiero decir: http://c.maysi.de/c6H9

Captura de pantalla : Introduzca aquí la descripción de la imagen

Como se puede ver hay algunos elementos que se agregaron al azar. ( RemoteViews que se agregaron a otro objeto RemoteViews ) Lo mismo sucede cuando cambio el tamaño del widget.

Las cosas que imprimí en el registro son como se esperaba. No hay datos incorrectos. Tampoco hay nuevas entradas de registro cuando me desplazo.

Este es mi código:

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

Todos los demás recursos se puede encontrar en el repo en GitHub.


Salida 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 

Así que los datos se pasan correctos. Simplemente no se muestra correctamente …

BTW: esto es lo que debería verse como: http://c.maysi.de/cB8K

¿Podría ser, que el problema se debe a las vistas remotas anidadas? Porque todas las vistas externas se muestran correctamente …

Su idea detrás del problema de reciclaje de ListView es correcta. Usted necesita entender cómo las cosas funcionan a nivel del suelo.

Modelo MVC

Los gráficos de Android funcionan en el patrón MVC, es decir, modelo-vista y patrón de controlador. El modelo es sus datos, base de datos en su caso, Ver es su disposición o porción gráfica, tal como ListView o RecyclerView o RemoteView. El controlador cambia su vista después de la actualización de los datos, la vista principal o ViewGroup en su caso el RemoteViewsService.RemoteViewsFactory es el controlador. Le sugiero que siga leyendo googleando el modelo MVC.

¿Cómo se implementa el patrón?

Cada vez que cambie los datos, la vista debe ser actualizada por el controlador. El marco de Android le da la oportunidad de mostrar su vista en la posición dada con sus datos sobrepasando getViewAt(int position) . El controlador llama a getViewAt(int position) para obtener la vista en la posición dada en ListView o RecyclerView. ListView o RecyclerView sólo visualiza filas visibles en la pantalla. Por ejemplo, si tiene 100 elementos en el ListView y sólo 7 es visible en la pantalla de lo que llamará getItemAt (int) 7 veces. Cada vez que se desplaza el getItemAt (int) se llama para las filas visibles. ListView y RemoteView se toman la libertad de reciclar / reutilizar la Vista pasada previamente devuelta por getItemAt (posición int). Asegura que la memoria consumida por la parte gráfica de su aplicación es limitada

¿Por qué hay un comportamiento extraño?

En primer lugar, cada cosa visible en la pantalla es una View como TextView, ImageView y ListView, etc. Si no, no se puede mostrar en la pantalla. RemoteView no es una vista. Pasa el diseño y los datos que se mostrarán con RemoteView (View + Data).

Aquí el me estoy refiriendo a su Screencast para la explicación.

1) Inicialización : El ListView en su caso, inicialmente crea decir 6 filas basado en el espacio visible en la pantalla y getViewAt(int position) se llama una vez si getCount() devuelve 1. Solicito chequear el valor devuelto de getCount () Del adaptador de lista.
2) Se desplazó hacia abajo : No pasa nada a ListView y el procesamiento de nuevas filas.
3) Se desplaza hacia arriba : getPositionAt (posición int) se llama de nuevo y RemoteView se pasa de nuevo. Dos filas son visibles ahora. Solicito chequear el valor devuelto getCount (). Debe ser 2 si no que la razón podría ser el almacenamiento en caché de filas por ListView.
4) Se desplazó hacia abajo : Nada sucede a la ListView y la prestación de nuevas filas.
5) Usted enrolló para arriba : Refiera 3. El getCount () debe ser 3 y así sucesivamente.

¿Qué debes hacer?

Según su implementación, usted creó el RemoteView sólo una vez y trató de reutilizar la misma vista en el getItemAt(int) , puede ser para ahorrar en el tiempo de inflación de diseño.
Para solucionar el problema, DEBE proporcionar el FRESH RemoteView cada vez que getItemAt(int) es llamado.

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

Yo mismo encontré la respuesta.

Para solucionar el problema con la extraña adición de vistas en el desplazamiento y el cambio de tamaño que tiene que llamar a removeAllViews en el diseño donde se agregaron las subcartes:

 @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; } 

Y el problema de que las vistas no se muestran es debido al color: Después de agregar

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

Se muestran todas las vistas:

Introduzca aquí la descripción de la imagen

Su problema parece estar en:

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

Lo está agregando dos veces .., pero sólo cuando el primer elemento no se ignora, por lo que parece aleatorio.

 stunden.add(new RemoteViews(context.getPackageName(), R.layout.fragment_stunde_widget)); 
  • TouchDelegate aplicado a ListView sólo responde al evento de clic
  • SetSelected () funciona buggy con ListView
  • Android ListView con OnItemClickListener Y GestureDetector
  • ListView.getCheckedItemPositions no puede devolver elementos marcados en SparseBooleanArray
  • Diferencias RelativeLayout entre 1,5 y 2,1
  • Android: ¿cómo actualizar el contenido de ListView?
  • Encabezados ListView de Android
  • Android: Centrar elementos en un ListVIew
  • Destruir vista (disposición)
  • ¿Cómo configurar onClickListener para partes separadas de la lista personalizada?
  • Usando simpleCursorAdapter
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.