J'ai un ListView avec différentes mises en page pour différents éléments. Certains éléments sont des séparateurs. Certains éléments sont différents car ils contiennent différents types de données, etc.
Je souhaite implémenter ViewHolders pour accélérer le processus getView, mais je ne sais pas trop comment s'y prendre. Des mises en page différentes contiennent des données différentes (ce qui rend difficile l'attribution de nom) et un nombre différent de vues que je souhaite utiliser.
Comment devrais-je m'y prendre?
La meilleure idée que je puisse proposer est de créer un ViewHolder générique avec X éléments, où X est le nombre de vues dans une présentation d'élément avec le plus grand nombre d'entre elles. Pour les autres vues avec un petit nombre de vues, je vais utiliser une sous-section de ces variables dans le ViewHolder. Alors disons que j'ai 2 mises en page que j'utilise pour 2 articles différents. L'un a 3 TextViews et l'autre 1. Je créerais un ViewHolder avec 3 variables TextView et n'en utiliserais qu'une seule pour mon autre élément. Mon problème est que cela peut devenir vraiment moche et se sentir vraiment hacky; surtout quand une mise en page d'élément peut avoir plusieurs vues de nombreux types différents.
Voici un getView très basique:
@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;
}
Supposons que je dispose de différents types de disposition de lignes, je pourrais avoir un ViewHolder pour chaque type de ligne. Mais quel type pourrais-je déclarer "titulaire" pour être au sommet? Ou devrais-je déclarer un titulaire pour chaque type, puis utiliser celui correspondant au type de rangée sur lequel je suis.
ListView a un système de gestion de types intégré. Dans votre adaptateur, vous avez plusieurs types d'éléments, chacun avec sa propre vue et sa propre présentation. En redéfinissant getItemViewType pour renvoyer le type de données d’une position donnée, ListView se fait un devoir de transmettre le convertview correct pour ce type de données. Ensuite, dans votre méthode getView, vérifiez simplement le type de données et utilisez une instruction switch pour gérer chaque type différemment.
Chaque type de disposition doit avoir son propre visualiseur pour nommer la clarté et la facilité de maintenance. Nommez les ViewHolders quelque chose lié à chaque type de données pour que tout soit clair.
Essayer de tout superposer dans un ViewHolder ne vaut tout simplement pas la peine.
Edit Exemple
@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
}
}
Un autre exemple.
classe publique CardArrayAdapter (extension) 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);
}
}
}