View Elemente recyceln (ViewHolder) wenn eine ListView über einen CursorAdapter gefüllt wird?

  • Antworten:2
  • Bentwortet
BlackDroid
  • Forum-Beiträge: 18

26.10.2011, 14:56:23 via Website

Hallo zusammen,

ich bin etwas verwirrt bzw. bin ich mir bei folgender Sache unsicher. Ich binde einen eigenen bzw. erweiterten CursorAdapter an eine ListView (ListFragment). Die Daten werden über einen erweiterten AsyncTaskLoader (LoaderManager) abgeholt und dann dem Adapter übergeben.

Ich nutze das Android Comp. Package v4.

Durch den AsyncTaskLoader werden die Daten im Hintergrund abgeholt - somit läuft der Vorgang nicht im UI Thread...also sicher.
Wo ich mir nicht sicher bin ist bei meinen View Objekten in der Liste - in meinem Buch steht beschrieben das man seine View-Objekte recyclen soll bzw. diese sollen in einem ViewHolder Objekt gespeichert werden. Das soll den Speicherbedarf und die Darstellung in der Liste verbessern - da die Referenzierung via findViewById nur einmal passiert, und danach eben im ViewHolder gespeichert wird. siehe Abschnitt Performance
http://de.wikibooks.org/wiki/Googles_Android_-_Lehrbuch_zur_Programmierung:_ListActivity

Allerdings beziehen sich diese Beispiele immer auf die Ableitung vom Typ BaseAdapter bzw. überschreiben der Methode getView()

Beim Ableiten von der Klasse CursorAdapter kümmerst sich anscheinend das Framework um das recyceln der Views:
http://codereview.stackexchange.com/questions/1057/android-custom-cursor-adapter-design
http://stackoverflow.com/questions/4567969/viewholder-pattern-correctly-implemented-in-custom-cursoradapter

1public class EnMeterListingFragment extends ListFragment
2implements LoaderManager.LoaderCallbacks<Cursor> {
3
4 private EnMeterListingAdapter mEnMeterListingAdapter;
5
6 @Override
7 public void onCreate(Bundle savedInstanceState) {
8 super.onCreate(savedInstanceState);
9
10 getLoaderManager().initLoader(0, null, this);
11 mEnMeterListingAdapter = new EnMeterListingAdapter(getActivity().getApplicationContext(), null);
12 setListAdapter(mEnMeterListingAdapter);
13 }
14 public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
15 return new EnMeterLoader(getActivity());
16 }
17
18 public void onLoadFinished(Loader<Cursor> loader, Cursor _cursor) {
19 mEnMeterListingAdapter.changeCursor(_cursor);
20 }
21
22 public void onLoaderReset(Loader<Cursor> loader) {
23 mEnMeterListingAdapter.changeCursor(null);
24 }
25}

1public class EnMeterListingAdapter extends CursorAdapter {
2 private final LayoutInflater mInflater;
3
4 public EnMeterListingAdapter(Context _context, Cursor _cursor) {
5 super(_context, _cursor);
6 mInflater = LayoutInflater.from(_context);
7 }
8
9 @Override
10 public View newView(Context _context, Cursor _cursor, ViewGroup _parent) {
11 final View view = mInflater.inflate(R.layout.enmeter_listing_item, _parent, false);
12 return view;
13 }
14
15 @Override
16 public void bindView(View _convertView, Context _context, Cursor _cursor) {
17
18 TextView txt_enmeter_listing_inst_point = (TextView) _convertView.findViewById(R.id.txt_enmeter_listing_inst_point);
19 TextView txt_enmeter_listing_mpname = (TextView) _convertView.findViewById(R.id.txt_enmeter_listing_mpname);
20
21 txt_enmeter_listing_inst_point.setText(_cursor.getString(_cursor.getColumnIndex(EnMeterTable.InstallationPoint)));
22 txt_enmeter_listing_mpname.setText(_cursor.getString(_cursor.getColumnIndex(EnMeterTable.MPName)));
23 }

Allerdings wird doch bei jedem bindView() erneut die findViewById()-Methode aufgerufen - also wo ist da der Unterschied bzw. warum kann ich mir in diesem Fall das recyclen sparen? Das würde ich gerne verstehen....

Als Newbie bin ich für jegliche Kritik sowieso immer offen - hoffe das die Umsetzung so sauber ist.

Viele Grüße

— geändert am 26.10.2011, 15:10:43

Antworten
Gelöschter Account
  • Forum-Beiträge: 294

26.10.2011, 16:16:52 via Website

* Android recycled die Views - nicht Du.

* Extended CursorAdapter und deren abgeleitete Klassen werden etwas anders behandelt als zum Beispiel BaseAdapter. Es kommt darauf an was Du machen willst. In den meisten Fällen, in denen es um reine Formatierung oder Bildbeschaffung geht, reicht der setViewValue. Dann brauchst Du auch keinen ViewHolder. Du legst in solchen Fällen den Adapter mit dem Cursor, dem Row-Layout und den from[] sowie to[] Werten im onLoadFinished (oder onPostExecute nach alter Art) an. Ist es erheblich komplexer dann sieht das etwas anders aus. In 90% der Fälle reicht das aber.

Es gibt im Web oder auch in ein paar Büchern ein paar lustige Empfehlungen die sich über die Zeit immer weiter kopiert haben. Oft sehe ich im setViewValue grausame Verrenkungen um eine Entscheidung vorzunehmen welches Objekt an der Reihe ist. Die meisten nehmen den columnIndex des Cursors - für den es in der Regel keinen Label gibt. Mit der View-Id geht das erheblich schneller und fehler unanfälliger. Ein Beispiel mit einem erweiterten SimpleCursorAdapter. Das habe ich jetzt einfach mal zusammengebaut - frag also nicht nach dem Sinn:

1public class MyAdapter extends SimpleCursorAdapter {
2
3 private class MyAdapterViewBinder implements MyAdapter.ViewBinder {
4
5 public boolean setViewValue(final View view, final Cursor cursor, final int columnIndex) {
6 long viewId = view.getId();
7
8 long id = cursor.getLong(columnIndexId);
9
10 if (viewId == R.id.myactivity_row_image) {
11 ImageView imageView = (ImageView) view;
12 ...
13 return true;
14 } else if (viewId == R.id.myactivity_row_text) {
15 TextView textView = (TextView) view;
16 ...
17 return true;
18 }
19
20 return false;
21 }
22 }
23
24 private int columnIndexId;
25
26 public MyAdapter(final Context context, final int layout, final Cursor cursor, final String[] from, final int[] to) {
27 super(context, layout, cursor, from, to);
28 ...
29 columnIndexId = cursor.getColumnIndex("_id");
30 ...
31 setViewBinder(new MyAdapterViewBinder());
32 }
33 }

Antworten
BlackDroid
  • Forum-Beiträge: 18

02.11.2011, 15:04:30 via Website

vielen Dank! - hat mir sehr geholfen.

das was ich beschrieben habe mit der "angeratenen" ViewHolder Implementierung wird wohl wirklich nur dann notwendig, wenn der Adapter vom Typ BaseAdapter und desen Ableitungen.

Antworten