2011-12-06 12 views
12

Ho un adattatore personalizzato che visualizza ogni riga nell'elenco di ordini.BaseAdapter notifyDatasetChanged() chiamato ma getView() non viene mai chiamato

public class OrderRowAdapter extends BaseAdapter implements OnClickListener { 
    OrderList items_; 
    LayoutInflater inflater_; 
    int list_view_resource_id_; 
    private final String TAG = "OrderRowAdapter"; 

    public OrderRowAdapter(Context context, int list_view_resource_id, 
      OrderList items) { 
     this.list_view_resource_id_ = list_view_resource_id; 
     this.items_ = items; 
     this.inflater_ = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    public Object getItem(int position) { 
     return items_.getOrders(position); 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     Log.d(TAG, "View updated for item in position = " + position); 

     View v = convertView; 
     if (v == null) { 
      v = inflater_.inflate(list_view_resource_id_, parent); 
     } 

     Order item = items_.getOrders(position); 
     if (item != null) { 
      TextView order_info_tv = (TextView) v.findViewById(R.id.order_info); 
      TextView order_status_tv = (TextView) v.findViewById(R.id.order_status); 

      if (order_info_tv != null) { 
       order_info_tv.setText(
         String.format("For customer: %s\nTotal of %d items", item.getCustomerId(), item.getItemsCount())); 
      } 
      if (order_status_tv != null) { 
       order_status_tv.setText("Status: " + getStatusText(item.getStatus())); 
      } 
     } 
     return v; 
    } 

    public int getCount() { 
     if (items_ == null) { 
      Log.d(TAG, "Null so get count returned 0"); 
      return 0; 
     } else { 
      Log.d(TAG, "Get count returned " + items_.getOrdersCount()); 
      return items_.getOrdersCount(); 
     } 
    }; 

Dopo l'interrogazione nuovo elenco di ordini da un servizio web, voglio aggiornare il contenuto del ListView, quindi ho la mia attività fa l'aggiornamento prima di chiamare notifyDataSetChanged()

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.orders); 

    initThreading(); 
    findViews(); 
    setUrls(); 

    // Load the list of order from disk 
    try { 
     order_list_ = OrderList.parseFrom(new FileInputStream(
       "/sdcard/orderList.bin")); 
    } catch (FileNotFoundException e) { 
     Log.e(TAG, "Cannot find the file", e); 
    } catch (IOException e) { 
     Log.e(TAG, "Cannot read the file", e); 
    } 

    order_row_adapter_ = new OrderRowAdapter(OrderActivity.this, 
      R.layout.order_row, order_list_); 
    orders_listview_.setAdapter(order_row_adapter_); 

    // Request new updates from the server 
    updateOrderInformation(-1); 
} 

public void updateOrders(InputStream new_order_stream) { 
    Log.d(TAG, "Updating order UI"); 
    try { 
     order_list_.parseFrom(new_order_stream); 
    } catch (IOException e) { 
     Log.e(TAG, "IOException" , e); 
    } 

    runOnUiThread(new Runnable() { 
     public void run() { 
      guiUpdateOrders(); 
     } 
    }); 
} 

private void guiUpdateOrders() { 
    order_row_adapter_.notifyDataSetChanged(); 
    Log.d(TAG, "Dataset notified that it has changed. GUI update anytime now."); 
} 

Ma, il metodo getView() di OrderRowAdapter non viene mai chiamato. Il ListView non viene mai aggiornato.

risposta

12

Si scopre il problema con il mio getView() non viene chiamato perché non è visibile. Il mio layout xml ha il TextView superiore con fill_parent per la sua altezza. Quindi l'intera vista ha solo quel singolo TextView visibile.

Soluzione: Controllare la vista grafica del layout in questione per assicurarsi che sia visibile lo ListView.

+0

IMO questo è un problema molto ovvio. Soprattutto perché nascondere un 'AdapterView' per visualizzare un' Spinner' è ancora un paradigma comune, posso vedere che è quella fastidiosa oca selvatica per un sacco di gente ... – alexgophermix

1

Per modificare il contenuto di ListView, è necessario continuare a utilizzare lo stesso riferimento all'elenco. Qui stai creando un altro elenco e assegnandolo alla variabile items_ (che non contiene l'elenco stesso, è solo un luogo in cui memorizzare un riferimento a un elenco), ma la tua vista ha ancora un riferimento al vecchio elenco.

Invece di items_ = new_order_list questo dovrebbe funzionare:

items_.clear(); 
items_.addAll(new_order_list); 

EDIT:

Per spiegare meglio, tenta di creare una nuova variabile denominata old_items:

public void setNewOrderList(List<Order> new_order_list) 
{ 
    Log.d(TAG, "New Order List available. Num items = " + new_order_list.size()); 

    List<Order> old_items = items_; // store the reference to the old list 
    items_ = new_order_list; 

    Log.d(TAG, "items_.size() = " + items_.size()); 
    Log.d(TAG, "old_items.size() = " + old_items.size()); // The old list still exists, and it's the one used by your ListView 

    notifyDataSetChanged(); 
} 
+0

Anche così, il metodo getView non dovrebbe essere chiamato se getCount continua a restituire 5? Ho provato il tuo suggerimento e ho ottenuto java.lang.UnsupportedOperationException su java.util.Collections $ UnmodifiableCollection.clear –

+0

Il metodo 'getCount()' conta gli elementi contenuti nel tuo nuovo elenco da quando hai sostituito il riferimento, non quello vecchio che è perso da qualche parte nella memoria. Ho modificato la mia risposta. – Dalmas

+0

L'eccezione indica che l'elenco è di sola lettura. Come lo crei? – Dalmas

2

Assicurarsi BaseAdapter metodi

registerDataSetObserver(DataSetObserver observer) 
unregisterDataSetObserver(DataSetObserver observer) 

non prevalgano.

0

Se tutte le risposte di cui sopra non provare con invalidateViews di lavoro()

ListView.invalidateViews() viene utilizzato per indicare il ListView per invalidare tutte le sue viste elemento figlio (li ridisegnare).

Si noti che non è necessario un numero di visualizzazioni uguale rispetto agli articoli. Questo perché una ListView ricicla le sue visualizzazioni degli oggetti e li sposta sullo schermo in modo intelligente mentre scorri.

listView.invalidateViews() 

mio esempio di implementazione,

    lvitems.post(new Runnable() { 
         @Override 
         public void run() { 
          lvitems.invalidateViews(); //invalidate old 
          CustomAdapter customAdapter=new CustomAdapter(context, names, images); // load new data 
          customAdapter.notifyDataSetChanged();// call notifydatasetChanged 
         } 
        }); 

Se qualche errore in questa risposta si prega di correggere i miei errori.

Problemi correlati