Erstellen von ViewHolders für ListViews mit verschiedenen Elementlayouts

Ich habe ein ListView mit verschiedenen Layouts für verschiedene Elemente. Einige Elemente sind Trennzeichen. Einige Elemente sind unterschiedlich, da sie verschiedene Arten von Daten usw. enthalten.

Ich möchte ViewHolders implementieren, um den getView-process zu beschleunigen, aber ich bin mir nicht sicher, wie ich vorgehen soll. Unterschiedliche Layouts haben unterschiedliche Daten (was die Benennung erschwert) und unterschiedliche Anzahlen von Ansichten, die ich verwenden möchte.

Wie soll ich das machen?

Die beste Idee, die ich mir vorstellen kann, besteht darin, einen generischen ViewHolder mit X-Elementen zu erstellen, wobei X die Anzahl der Ansichten in einem Elementlayout mit der höchsten Anzahl von Elementen angibt. Für die anderen Ansichten mit einer kleinen Anzahl von Ansichten verwende ich nur einen Unterabschnitt dieser Variablen im ViewHolder. Also sage ich, ich habe 2 Layouts, die ich für 2 verschiedene Gegenstände verwende. Eins hat 3 TextViews und das andere hat 1. Ich würde einen ViewHolder mit 3 TextView-Variablen erstellen und nur 1 von ihnen für meinen anderen Artikel verwenden. Mein Problem ist, dass das wirklich hässlich aussehen kann und sich wirklich hacky anfühlt; vor allem, wenn ein Artikellayout viele Ansichten vieler verschiedener Typen haben kann.

Hier ist ein sehr einfaches getView:

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

Angenommen, ich hätte verschiedene Arten von Zeilenlayouts, könnte ich einen ViewHolder für jeden Typ von Zeile haben. Aber welchen Typ würde ich “Halter” nennen, um an der Spitze zu sein? Oder würde ich einen Halter für jeden Typ deklarieren und dann den für den Typ der Reihe verwenden, in der ich bin.

ListView verfügt über ein eingebautes Typ-Management-System. In Ihrem Adapter haben Sie verschiedene Arten von Elementen, die jeweils ihre eigene Ansicht und ihr eigenes Layout haben. Durch Überschreiben von getItemViewType , um den Datentyp einer bestimmten Position zurückzugeben, wird ListView dazu gezwungen, das richtige Conversionview für diesen Datentyp zu übergeben. Dann überprüfen Sie in Ihrer getView-Methode einfach den Datentyp und verwenden eine switch-statement, um jeden Typ anders zu behandeln.

Jeder Layouttyp sollte einen eigenen Viewholder haben, um Klarheit und Wartungsfreundlichkeit zu gewährleisten. Benennen Sie die ViewHolders mit etwas, das zu jedem Datentyp gehört, um alles klar zu halten.

Der Versuch, alles in einen ViewHolder zu überlagern, lohnt sich nicht.

Beispiel bearbeiten

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

Ein anderes Beispiel.

Öffentliche class CardArrayAdapter erweitert 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); } } 

}