2009-11-02 13 views
10

Używam rozszerzonej wersji BaseAdapter na podstawie EfficientAdapter example z próbek demonstracyjnych SDK.Android: EfficientAdapter z dwoma różnymi widokami

Moje dane to w zasadzie obiekt (ListPlaces) zawierający ArrayList z rzeczywistą listą miejsc, dostępną przez listPlaces.getValues(). Dane ArrayList są sortowane według zakresu, a ArrayList składa się z kilku specjalnych elementów (separatorów), bez danych, ale z flagą separator ustawioną na true.

Teraz kiedy mój EfficientAdapter dostaje obiektu danych, który jest separator zwraca false dla public boolean isEnabled(int position) i public View getView(int position, View convertView, ViewGroup parent) nadmuchuje dwa różne układy w zależności czy obecny obiekt danych składa się z rzeczywistymi danymi czy to tylko atrapa separator.

Działa to świetnie, jeśli za każdym razem nadmuchuję układ. Jednak zawyżanie układu za każdym razem i wywoływanie findViewById powoduje, że powolne staje się prawie niezauważalne.

Próbowałem więc użyć EfficientAdapter z podejściem ViewHolder. Ale to nie zadziałało zaraz po wyjęciu z pudełka, z powodu dwóch różnych widoków, do których próbuję uzyskać dostęp. Tak więc zawsze, gdy mój convertView != null (inny przypadek) uzyskuje dostęp do elementów w układzie za pomocą naszego ViewHolder i gdy poprzedni widok był separatorem, oczywiście nie działa, aby uzyskać dostęp do TextView, który jest dostępny tylko w "prawdziwym" układzie elementów .

Więc ja też zmusić mój getView() napompować układ nie tylko wtedy, gdy convertView == null, ale również wtedy, gdy poprzednia listRow jest inny niż bieżący: if (convertView == null || (listRow != listRow_previous)) { [....] }

To wydaje się prawie działa teraz. A przynajmniej nie rozbija się od samego początku. Ale nadal się zawiesza i nie wiem, co mam robić inaczej. Próbowałem zajrzeć do convertView.getID() i convertView.getResources(), ale to nie było naprawdę pomocne. Może ktoś inny ma pomysł, w jaki sposób mogę sprawdzić, czy mój aktualny convertView pasuje do układu elementu listy lub układu separatora listy. Dzięki.

Oto kod. Gdzie nigdy nie jest [...] Wyjąłem trochę mniej ważny kod, aby ułatwić czytać i rozumieć:

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

I tu moje Logcat wyjściowa:

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

Czy możesz dodać komentarz pokazujący, gdzie linia 300 znajduje się w stosie? Jeśli jest 'if (curValues.separator> = 0)', czy możesz obsłużyć pozycje upto 1 na końcu listy? – rsp

Odpowiedz

12

zapomniałeś kilka metod, które należy zastąpić: getViewTypeCount() i getItemViewType(). Nie są one potrzebne w przypadku list, w których wszystkie wiersze są takie same, ale są bardzo ważne dla twojego scenariusza. Zastosuj je prawidłowo, a Android będzie utrzymywał oddzielne pule obiektów dla twoich nagłówków i wierszy szczegółów.

Albo, można spojrzeć na:

+1

Dziękujemy za szybką odpowiedź. Wskazówka z getViewTypeCount() i getItemViewType() była bardzo przydatna. Uwielbiam ten adapter. Dziękuję również za udostępnienie kodu źródłowego twojego i Jeffa Sharkeya. Spojrzałem na to już w zeszłym tygodniu, ale ponieważ obecnie pracuję nad projektem o zamkniętym źródle, nie jestem w stanie wdrożyć przynajmniej przykładów GPL. Dlatego postanowiłem rozpocząć własną implementację, która w końcu działa teraz dzięki waszej pomocy. Bardzo dziękuję! – znq

+0

zaktualizowany link do wydań przestał działać. nie można znaleźć łącza, które nadal działa – rtack

6

Dzięki podpowiedzi z getViewTypeCount() i getItemViewType() działa teraz idealnie.

Wdrożenie tych dwóch metod była bardzo prosta:

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

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

Jak commonsware wspomniano w swojej odpowiedzi w ten sposób Android będzie utrzymywać różne pule obiektów dla różnych elementów listy, co oznacza również można usunąć czek na listRow_previous w moim przykładzie i zmień tylko if (convertView == null || (listRow != listRow_previous)) na if (convertView == null).

Powiązane problemy