2010-08-18 14 views
46

Ho una ListView con layout diversi per articoli diversi. Alcuni oggetti sono separatori. Alcuni articoli sono diversi perché contengono diversi tipi di dati, ecc.Creazione di ViewHolders per ListViews con layout di elementi diversi

Desidero implementare ViewHolders per accelerare il processo getView, ma non sono abbastanza sicuro su come procedere. Layout diversi hanno dati diversi (il che rende difficile nominare) e diversi numeri di Views che voglio usare.

Come dovrei andare a fare questo?

L'idea migliore che riesco a fare è creare un ViewHolder generico con elementi X dove X è il numero di Visualizzazioni in un layout di articolo con il numero più alto di esse. Per le altre viste con un numero ridotto di viste, userò solo una sottosezione di queste variabili nel ViewHolder. Quindi dire che ho 2 layout che uso per 2 elementi diversi. Uno ha 3 TextViews e l'altro ha 1. Vorrei creare un ViewHolder con 3 variabili TextView e usarne solo 1 per il mio altro oggetto. Il mio problema è che questo può diventare davvero brutto e sembra davvero hacky; specialmente quando un layout di un articolo può avere molte viste di molti tipi diversi.

Ecco un GetView molto semplice:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 

    MyHolder holder; 

    View v = convertView; 
    if (v == null) { 
     LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     v = vi.inflate(R.layout.layout_mylistlist_item, parent, false); 

     holder = new MyHolder(); 
     holder.text = (TextView) v.findViewById(R.id.mylist_itemname); 
     v.setTag(holder); 
    } 
    else { 
     holder = (MyHolder)v.getTag(); 
    } 

    MyListItem myItem = m_items.get(position); 

    // set up the list item 
    if (myItem != null) { 
     // set item text 
     if (holder.text != null) { 
      holder.text.setText(myItem.getItemName()); 
     } 
    } 

    // return the created view 
    return v; 
} 

Supponiamo avevo diversi tipi di layout di riga, che potrebbe avere un ViewHolder per ogni tipo di riga. Ma che tipo dichiarerei "titolare" in cima? O dovrei dichiarare un detentore per ogni tipo e quindi usare quello per il tipo di riga in cui mi trovo.

risposta

105

ListView dispone di un sistema di gestione dei tipi integrato. Nel tuo adattatore, hai diversi tipi di oggetti, ciascuno con la propria vista e layout. Sovrascrivendo getItemViewType per restituire il tipo di dati di una determinata posizione, ListView è autorizzato a passare nella conversione convertita corretta per quel tipo di dati. Quindi, nel metodo getView, è sufficiente controllare il tipo di dati e utilizzare un'istruzione switch per gestire ogni tipo in modo diverso.

Ogni tipo di Layout deve avere il proprio viewholder per la chiarezza dei nomi e la facilità di manutenzione. Assegna al ViewHolders qualcosa relativo a ciascun tipo di dati per mantenere tutto in ordine.

Cercare di sovrapporre tutto in un solo ViewHolder non vale la pena.

Modifica Esempio

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    int viewType = this.getItemViewType(position); 

    switch(viewType) 
    { 
     case TYPE1: 

     Type1Holder holder1; 

     View v = convertView; 
     if (v == null) { 
      LayoutInflater vi = (LayoutInflater)getContext().getSystemService  (Context.LAYOUT_INFLATER_SERVICE); 
      v = vi.inflate(R.layout.layout_mylistlist_item_type_1, parent, false); 

      holder1 = new Type1Holder(); 
      holder1.text = (TextView) v.findViewById(R.id.mylist_itemname); 
      v.setTag(holder1); 
     } 
     else { 
      holder1 = (Type1Holder)v.getTag(); 
     } 

     MyListItem myItem = m_items.get(position); 

     // set up the list item 
     if (myItem != null) { 
      // set item text 
      if (holder1.text != null) { 
       holder1.text.setText(myItem.getItemName()); 
      } 
     } 

     // return the created view 
     return v; 


    case TYPE2: 
      Type2Holder holder2; 

     View v = convertView; 
     if (v == null) { 
      LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      v = vi.inflate(R.layout.layout_mylistlist_item_type_2, parent, false); 

      holder2 = new Type2Holder(); 
      holder2.text = (TextView) v.findViewById(R.id.mylist_itemname); 
      holder2.icon = (ImageView) v.findViewById(R.id.mylist_itemicon); 
      v.setTag(holder1); 
     } 
     else { 
      holder2 = (Type2Holder)v.getTag(); 
     } 

     MyListItem myItem = m_items.get(position); 

     // set up the list item 
     if (myItem != null) { 
      // set item text 
      if (holder2.text != null) { 
       holder2.text.setText(myItem.getItemName()); 
      } 

      if(holder2.icon != null) 
       holder2.icon.setDrawable(R.drawable.icon1); 
     } 


     // return the created view 
     return v; 


     default: 
      //Throw exception, unknown data type 
    } 
} 
+0

Forse ho bisogno di un po 'di spazzolatura sul mio Java allora. All'inizio delle funzioni getView nel mio ListActivities, istanziato il mio detentore definito per quel ListView. Quindi controllo convertView per null e getTag o crea il titolare e imposta gli elementi. Se ho intenzione di utilizzare diversi titolari per i tipi di articoli, come devo dichiarare il mio titolare come all'inizio di getView? O devo istanziare 1 per tutti i miei tipi di oggetto e inizializzare e usare quello che corrisponde al tipo di riga? – Andrew

+0

Si prega di vedere il mio post modificato in alto – Andrew

+0

@Andrew, ogni titolare sarebbe individualmente definito in una delle dichiarazioni del caso. Inserisci tutto il codice esistente nell'istruzione case ecah, ma modifica il tipo di viewholder e la gestione dei dati per ognuno. Modificherò la mia risposta con un esempio in un po '. – CodeFusionMobile

0

Un altro esempio.

public class CardArrayAdapter estende ArrayAdapter {

public CardArrayAdapter(Context context) { 
    super(context, R.layout.adapter_card); 
} 

@Override 
public View getView(int position, View view, ViewGroup parent) { 

    final Card card = getItem(position); 
    ViewHolder holder; 

    //if (view != null) { 
     //holder = (ViewHolder) view.getTag(); 
    //} else { 
    Log.d("card.important?", card.name + " = " + Boolean.toString(card.important)); 
    if(card.important) { 
     view = LayoutInflater.from(getContext()).inflate(R.layout.adapter_card_important, parent, false); 
    }else { 
     view = LayoutInflater.from(getContext()).inflate(R.layout.adapter_card, parent, false); 
    } 
    holder = new ViewHolder(view); 
    view.setTag(holder); 
    //} 

    // IMG 
    Picasso.with(getContext()) 
      .load(card.logo) 
      .placeholder(R.drawable.ic_phonebook) 
      .error(R.drawable.ic_phonebook) 
      .fit() 
      .centerCrop() 
      .transform(new CircleTransform()) 
      .into(holder.logo); 

    holder.name.setText(card.name); 

    if(card.important) { 
     holder.category.setVisibility(View.VISIBLE); 
     if (card.category.equals("airline")) { 
      card.category = "airlines"; 
     } 
     int id = getContext().getResources().getIdentifier(card.category, "string", getContext().getPackageName()); 
     holder.category.setText(getContext().getResources().getString(id)); 
    }else { 
     holder.category.setVisibility(View.GONE); 
    } 

    holder.tagline.setText(card.tagline); 
    holder.favorite.setChecked(card.favorite); 

    holder.favorite.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      card.favorite = ((CheckBox) v).isChecked(); 
      card.save(); 
     } 
    }); 

    return view; 

} 

static class ViewHolder { 
    @InjectView(R.id.logo) ImageView logo; 
    @InjectView(R.id.name) TextView name; 
    @InjectView(R.id.category) TextView category; 
    @InjectView(R.id.tagline) TextView tagline; 
    @InjectView(R.id.favorite) CheckBox favorite; 
    public ViewHolder(View view) { 
     ButterKnife.inject(this, view); 
    } 
} 

}

+0

Penso che funzionerà, ma renderà il viewholder meno utilizzabile, dal momento che ogni volta che si cambia tipo, si gonfia il nuovo layout – hakim

Problemi correlati