2010-08-18 20 views
46

Tengo un ListView con diferentes diseños para diferentes elementos. Algunos artículos son separadores. Algunos elementos son diferentes porque contienen diferentes tipos de datos, etc.Crear ViewHolders para ListViews con diferentes diseños de elementos

Quiero implementar ViewHolders para acelerar el proceso getView, pero no estoy muy seguro de cómo hacerlo. Diferentes diseños tienen diferentes datos (lo que hace que nombrar sea difícil) y diferentes números de Vistas que quiero usar.

¿Cómo debo hacer esto?

La mejor idea que se me ocurre es crear un ViewHolder genérico con X elementos donde X es el número de Vistas en un diseño de elemento con el mayor número de ellas. Para las otras vistas con un número pequeño de Vistas, solo usaré una subsección de esas variables en el ViewHolder. Entonces, digo que tengo 2 diseños que uso para 2 artículos diferentes. Uno tiene 3 TextViews y el otro 1. He creado un ViewHolder con 3 variables TextView y solo uso 1 de ellos para el otro elemento. Mi problema es que esto puede ser realmente feo y se siente realmente hacky; especialmente cuando el diseño de un elemento puede tener muchas Vistas de muchos tipos diferentes.

Aquí es una getView muy básico:

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

Supongamos que tenía diferentes tipos de diseños de filas, que podría tener una ViewHolder para cada tipo de fila. ¿Pero qué tipo declararía "titular" para estar en la parte superior? O declararía un titular para cada tipo y luego usaría el que corresponde al tipo de fila en la que estoy.

Respuesta

105

ListView tiene incorporado un sistema de gestión de tipos. En su adaptador, tiene varios tipos de elementos, cada uno con su propia vista y diseño. Al anular getItemViewType para devolver el tipo de datos de una posición determinada, ListView se transfiere para pasar la conversión de conversión correcta para ese tipo de datos. Luego, en su método getView simplemente verifique el tipo de datos y use una declaración de cambio para manejar cada tipo de manera diferente.

Cada tipo de diseño debe tener su propio visor para dar nombre a la claridad y la facilidad de mantenimiento. Denomine a ViewHolders algo relacionado con cada tipo de datos para mantener todo en orden.

Intentar superponer todo en un solo ViewHolder no vale la pena.

Editar Ejemplo

@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

Quizá necesito un poco de cepillado en mi Java a continuación. Al comienzo de las funciones de getView en mi ListActivities, creo una instancia de mi poseedor definido para ese ListView. Luego reviso convertView para null y getTag o creo el titular y establezco los elementos. Si voy a utilizar diferentes titulares para los tipos de elementos, ¿qué debería declarar mi titular como al inicio de getView? ¿O necesito crear una instancia 1 para todos mis tipos de elementos e inicializar y usar la que corresponde al tipo de fila? – Andrew

+0

Consulte mi publicación editada arriba – Andrew

+0

@Andrew, cada titular se definiría individualmente en una de las declaraciones de casos. Coloque todo su código existente en la declaración de caso ecah, pero cambie el tipo de usuario y el manejo de datos para cada uno. Editaré mi respuesta con un ejemplo en un momento. – CodeFusionMobile

0

Otro ejemplo.

CardArrayAdapter clase pública se extiende 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

Creo que esto funcionará, pero hace que el usuario sea menos útil, ya que cada vez que cambia de tipo, se infla el nuevo diseño – hakim

Cuestiones relacionadas