2009-11-02 14 views
10

Estoy usando una versión extendida de BaseAdapter basada en el EfficientAdapter example de las muestras de demostración de SDK.Android: EfficientAdapter con dos vistas diferentes

Mis datos son básicamente un objeto (ListPlaces) que contiene un ArrayList con la lista real de lugares, accesible a través de listPlaces.getValues(). Estos datos de ArrayList se ordenan por rango y ArrayList constan de algunos elementos especiales (separadores), sin datos, pero un indicador separator establecido en true.

Ahora cada vez que mi EfficientAdapter consigue un objeto de datos que es un separador vuelve false para public boolean isEnabled(int position) y public View getView(int position, View convertView, ViewGroup parent) infla dos diseños diferentes dependiendo de si el objeto de datos actual consiste en datos reales o es sólo un maniquí separador.

Esto funciona muy bien, si inflico el diseño cada vez. Sin embargo, inflar el diseño cada vez y llamar al findViewById hace que el ListView sea casi inútil.

Así que traté de utilizar el EfficientAdapter con el enfoque ViewHolder. Pero eso no funcionó de la caja, debido a las dos vistas diferentes a las que trato de acceder. Entonces cada vez que mi convertView != null (el otro caso) accede a los elementos en el diseño a través de nuestro ViewHolder y cuando la Vista anterior era un separador, por supuesto no funciona para acceder a un TextView allí que solo está disponible en el diseño de elementos "reales" .

Así que también obligar a mi getView() para inflar el diseño no sólo cuando convertView == null, sino también cuando el listRow anterior es diferente a la actual: if (convertView == null || (listRow != listRow_previous)) { [....] }

Esto parece casi funciona en este momento. O al menos no se bloquea desde el principio. Pero todavía se cuelga y no sé qué tengo que hacer diferente. Intenté buscar en convertView.getID() y convertView.getResources(), pero eso no fue de mucha ayuda hasta ahora. Tal vez alguien más tenga una idea de cómo puedo verificar si mi actual convertView coincide con el diseño de la lista de elementos o el diseño del separador de listas. Gracias.

Aquí está el código. Donde quiera que hay una [...] me sacó un poco de código es menos importante para que sea más fácil de leer y entender:

private class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private ListPlaces listPlaces; 

    private ListRow listRow; 
    private ListRow listRow_previous; 


    public EfficientAdapter(Context context, ListPlaces listPlaces) { 
     // Cache the LayoutInflate to avoid asking for a new one each time. 
     mInflater = LayoutInflater.from(context); 

     // Data 
     this.listPlaces = listPlaces; 
    } 

    /** 
    * The number of items in the list is determined by the number of items 
    * in our ArrayList 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return listPlaces.getValues().size(); 
    } 

    /** 
    * Since the data comes from an array, just returning the index is 
    * sufficent to get at the data. If we were using a more complex data 
    * structure, we would return whatever object represents one row in the 
    * list. 
    * 
    * @see android.widget.ListAdapter#getItem(int) 
    */ 
    public Object getItem(int position) { 
     return position; 
    } 

    /** 
    * Use the array index as a unique id. 
    * 
    * @see android.widget.ListAdapter#getItemId(int) 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    @Override 
    public boolean isEnabled(int position) { 
     // return false if item is a separator: 
     if(listPlaces.getValues().get(position).separator >= 0) 
      return false; 
     else 
      return true; 
    } 

    @Override 
    public boolean areAllItemsEnabled() { 
     return false;   
    } 



    /** 
    * Make a view to hold each row. 
    * 
    * @see android.widget.ListAdapter#getView(int, android.view.View, 
    *  android.view.ViewGroup) 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 

     // Get the values for the current list element 
     ListPlacesValues curValues = listPlaces.getValues().get(position); 
     if (curValues.separator >= 0) 
      listRow = ListRow.SEPARATOR; 
     else 
      listRow = ListRow.ITEM; 
     Log.i(TAG,"Adapter: getView("+position+") " + listRow + " (" + listRow_previous + ") -> START"); 

     // A ViewHolder keeps references to children views to avoid unneccessary calls 
     // to findViewById() on each row. 
     ViewHolder holder; 

     // When convertView is not null, we can reuse it directly, there is no need 
     // to reinflate it. We only inflate a new View when the convertView supplied 
     // by ListView is null. 
     if (convertView == null || (listRow != listRow_previous)) { 
      Log.i(TAG, "--> (convertView == null) at position: " + position); 
      // Creates a ViewHolder and store references to the two children views 
      // we want to bind data to. 
      holder = new ViewHolder(); 

      if (listRow == ListRow.SEPARATOR) { 
       convertView = mInflater.inflate(R.layout.taxonomy_list_separator, null); 
       holder.separatorText = (TextView) convertView.findViewById(R.id.separatorText); 
       convertView.setTag(holder); 
       Log.i(TAG,"\tCREATE SEPARATOR: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 

      } 
      else { 

       convertView = mInflater.inflate(R.layout.taxonomy_listitem, null); 
       holder.name = (TextView) convertView.findViewById(R.id.name); 
       holder.category = (TextView) convertView.findViewById(R.id.category); 
       // [...] 

       convertView.setTag(holder); 

       Log.i(TAG,"\tCREATE ITEM: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 
      } 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      Log.i(TAG,"\tconvertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 

      holder = (ViewHolder) convertView.getTag(); 
      convertView.setAnimation(null); 
     } 

     /* Bind the data efficiently with the holder */ 
     if (listRow == ListRow.SEPARATOR) { 
      String separatorText; 
      switch (curValues.separator) { 
      case 0: separatorText="case 0"; break; 
      case 1: separatorText="case 1"; break; 
      case 2: separatorText="case 2"; break; 
      // [...] 
     default: separatorText="[ERROR]"; break; 
      } 
      holder.separatorText.setText(separatorText); 
     } 
     else { 
      // Set the name: 
      holder.name.setText(curValues.name); 
      // Set category 
      String cat = curValues.classification.toString(); 
      cat = cat.substring(1,cat.length()-1); // removing "[" and "]" 
      if (cat.length() > 35) { 
       cat = cat.substring(0, 35); 
       cat = cat + "..."; 
      } 
      holder.category.setText(cat); 

      // [...] (and many more TextViews and ImageViews to be set) 

     } 

     listRow_previous = listRow; 
     Log.i(TAG,"Adapter: getView("+position+") -> DONE"); 
     return convertView; 
    } 

    private class ViewHolder { 
     TextView name; 
     TextView category; 
     // [...] -> many more TextViews and ImageViews 

     TextView separatorText; 
    } 
} 

Y aquí mi Logcat de salida:

755  ListPlaces_Activity I onPostExecute: notifyDataSetChanged()                         
    755  ListPlaces_Activity I Adapter: getView(0) SEPARATOR (null) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 0                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(0) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(1) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 1                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(1) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(2) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 2                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(2) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(3) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 3                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(3) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(4) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(4) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(5) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(5) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(6) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(6) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(0) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 0                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(0) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(1) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 1                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(1) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(2) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 2                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(2) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(3) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 3                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(3) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(4) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(4) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(5) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755   AndroidRuntime D Shutting down VM                              
    755    dalvikvm W threadid=3: thread exiting with uncaught exception (group=0x4001aa28)                 
    755   AndroidRuntime E Uncaught handler: thread main exiting due to uncaught exception                  
    755   AndroidRuntime E java.lang.NullPointerException                          
    755   AndroidRuntime E  at com.tato.main.ListPlaces_Activity$EfficientAdapter.getView(ListPlaces_Activity.java:330)          
    755   AndroidRuntime E  at android.widget.HeaderViewListAdapter.getView(HeaderViewListAdapter.java:191)             
    755   AndroidRuntime E  at android.widget.AbsListView.obtainView(AbsListView.java:1255)                 
    755   AndroidRuntime E  at android.widget.ListView.makeAndAddView(ListView.java:1658)                 
    755   AndroidRuntime E  at android.widget.ListView.fillDown(ListView.java:637)                   
    755   AndroidRuntime E  at android.widget.ListView.fillFromTop(ListView.java:694)                  
    755   AndroidRuntime E  at android.widget.ListView.layoutChildren(ListView.java:1502)                 
    755   AndroidRuntime E  at android.widget.AbsListView.onLayout(AbsListView.java:1112)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)               
    755   AndroidRuntime E  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)               
    755   AndroidRuntime E  at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)               
    755   AndroidRuntime E  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)               
    755   AndroidRuntime E  at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.view.ViewRoot.performTraversals(ViewRoot.java:979)                 
    755   AndroidRuntime E  at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)                  
    755   AndroidRuntime E  at android.os.Handler.dispatchMessage(Handler.java:99)                   
    755   AndroidRuntime E  at android.os.Looper.loop(Looper.java:123)                      
    755   AndroidRuntime E  at android.app.ActivityThread.main(ActivityThread.java:4203)                 
    755   AndroidRuntime E  at java.lang.reflect.Method.invokeNative(Native Method)                   
    755   AndroidRuntime E  at java.lang.reflect.Method.invoke(Method.java:521)                    
    755   AndroidRuntime E  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)            
    755   AndroidRuntime E  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)                 
    755   AndroidRuntime E  at dalvik.system.NativeStart.main(Native Method)  
+0

¿Podría agregar un comentario que muestre dónde está la línea 300 que está en la pila? Si es 'if (curValues.separator> = 0)', ¿debería poder manejar las posiciones hasta 1 al final de la lista? – rsp

Respuesta

12

Se le olvidó un par de métodos que necesita anular: getViewTypeCount() y getItemViewType(). No son necesarios para listas donde todas las filas son iguales, pero son muy importantes para su escenario. Implemente estos correctamente, y Android mantendrá grupos de objetos separados para sus encabezados y filas de detalles.

O, usted podría mirar a:

+1

Gracias por su rápida respuesta. La sugerencia con getViewTypeCount() y getItemViewType() fue muy útil. Me encanta este adaptador. Gracias también por compartir su código fuente y Jeff Sharkey. Ya lo vi la semana pasada, pero dado que actualmente estoy trabajando en un proyecto de código cerrado, no puedo implementar al menos los ejemplos de GPL. Así que decidí comenzar mi propia implementación, que finalmente funciona ahora gracias a su ayuda. ¡Muchas gracias! – znq

+0

el enlace de representación actualizado ya no funciona. no pude encontrar el enlace que todavía funciona – rtack

6

Gracias a la sugerencia de getViewTypeCount() y getItemViewType() funciona perfectamente ahora.

La implementación de estos dos métodos fue muy simple:

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

@Override 
public int getItemViewType(int position) { 
if(listPlaces.getValues().get(position).separator >= 0) 
    return 0; 
else 
    return 1; 
} 

Como commonsware menciona en su respuesta de esta manera androide mantendrá diferentes piscinas de objetos para diferentes elementos de la lista, que también significa que usted puede quitar el cheque por listRow_previous en mi ejemplo y cambie el if (convertView == null || (listRow != listRow_previous)) a if (convertView == null) solamente.

Cuestiones relacionadas