Android ListFragment per SimpleCursorAdapter automatisch neufüllen

kurztipp

Aktives Mitglied
Hallo,

ich habe eine Activity, die ein ListFragment enthält, das die Daten eines SimpleCursorAdapter ausgibt. Ich möchte, die Daten aus der Datenbank anhand ihrer IDs abfragen.
Die IDs bekomme ich allerdings durch einen AlertDialog, d.h. ich muss diese irgendwie an den CursorAdapter weitergeben, denn wenn eine neue ID ausgewählt wurde, soll das ListFragment um den neuen Datensatz erweitert werden. (Ähnlich hier: Loaders | Android Developers)

Gedachter Ablauf also: AlertDialog liefert ID -> ID an SimpleCursorAdapter oder Loader -> Update des ListFragments.

Mein Problem ist, dass CursorLoader scheinbar einen Content Provider benötigt.
Kann ich Loader auch schlicht mit Datenbanken benutzen? Wenn ja, wie?

Gruß
 

dzim

Top Contributor
Im Loader brauchst du schon einen ContentProvider und ich würde empfehlen, den auch zu verwenden. Die meisten deiner Datenbankoperationen werden einfache Selects/Inserts/Updates/Deletes sein und da ist das Teil schon sehr praktisch, auch wenn der initialle Aufwand zugegeben recht hoch ist, bevor was damit passieren kann.
Ich verwende zwar durchaus auch Plain-SQL-Queries, aber das nur in "Extremsituationen" - Joins finde ich über ContentProvider etwas umständlich, aber vor allem bei komplexen Operationen bin ich da bei meinen Erfahrungen besser dran.
Aber um es kurz zu machen: Normalerweise ist bei ListViews *immer* der ContentProvider zu verwenden (abstrakter, in Theorie zumindest einfacher auszutauschen). Und Suchen nach IDs ist eigentlich immer recht simpel - du musst dann beim Konstruieren des Adapters halt als Select die "_id" deiner "from"-Tabelle verwenden und als Parameter halt so was wie [c]new String(){Long.toString(yourID)}[/c] angeben.

Ist das in etwa verständlich gewesen?
 

kurztipp

Aktives Mitglied
Hallo,

Ist das in etwa verständlich gewesen?
Leider nicht ;)

Im Loader brauchst du schon einen ContentProvider und ich würde empfehlen, den auch zu verwenden.
Wen? Den Loader oder ContentProvider? Ich kann doch einen Loader gar nicht ohne CP verwenden oder hab ich da was in der Dokumentation überlesen?

Aber um es kurz zu machen: Normalerweise ist bei ListViews *immer* der ContentProvider zu verwenden
Wieso? Wenn ich nur an Daten aus meiner eigenen sqlite Datenbank möchte, ist doch ein SimpleCursorAdapter völlig ausreichend? Schließlich zeigt ja:
Java:
public class SimpleListFragment extends ListFragment{
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		String[] colums = new String[]{"spalte1", "spalte2"/*, ...*/};
		int[] to = new int[]{R.id.1, R.id.1/*,...*/};
		
		DBContract mDbHelper = new DBContract(getActivity());
		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		Cursor cursor = db.query(TABLE_NAME, colums, null, null, null, null, null);
		
		ListAdapter adapter = new SimpleCursorAdapter(getActivity(),
				R.layout.simple_list_layout, cursor, from, to, 0);
		setListAdapter(adapter);
	}

}
alles an, was man will, wenn die Daten nicht aktualisiert werden müssen.

Und Suchen nach IDs ist eigentlich immer recht simpel - du musst dann beim Konstruieren des Adapters halt als Select die "_id" deiner "from"-Tabelle verwenden und als Parameter halt so was wie [c]new String(){Long.toString(yourID)}[/c] angeben.
Das hab ich mir schon gedacht. Im Endeffekt muss ich also einen ContentProvider schreiben, um den Loader benutzen zu können und damit das automatische aktualisieren der Daten zu erreichen?


Gruß
 
Zuletzt bearbeitet:

kurztipp

Aktives Mitglied
Hallo,

mein Ansatz hat sich etwas verändert: Dialog liefert ID -> Suche der Daten anhand der ID -> erzeugen einer Instanz einer eigenen Klasse mit den Daten. Das macht mehr Sinn, da ich erst mit den Daten weiterarbeiten muss, bevor ich sie anzeigen kann.
Jetzt stellt sich nur die Frage, wie die ListView am besten aktualisiere. Mein Ansatz bisher:
Java:
/**
 * The dialog fragment receives a reference to this Activity through the
 * Fragment.onAttach() callback, which it uses to call the following method
 */
@Override
public void onListItemClick(AdapterView<?> parent, View v, int position,
		long id) {
	Cursor cursor = db.query(TABLE_NAME, null, "_id=?",
			new String[] { String.valueOf(id) }, null, null, null);

	if (cursor.moveToFirst()) {
		double colA = cursor.getDouble(1);
		double colD = cursor.getDouble(4);
		mSampleObjects.add(new SampleObject(colA, colD));
	}
	updateListView();
}

private void updateListView() {
	for (SampleObject so : mSampleObjects) {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put("valA", Double.toString(so.getValA()));
		map.put("valD", Double.toString(so.getValD()));
		mAdapterList.add(map);
	}

	String[] from = new String[] { "valA", "valD"};
	int[] to = new int[] { R.id.val_a, R.id.val_d};

	ListAdapter adapter = new SimpleAdapter(this, list,
			R.layout.fragment_dish_ingredients, from, to);

	setListAdapter(adapter);
}

Das funktioniert soweit auch, aber ich denke, das eher eine Art "quick and dirty" Lösung, denn es wird ein neuer Adapter initiiert und an die Liste übergeben, d.h. es wird wahrscheinlich auch die gesamte Liste von der Activity unnötigerweise neu initiiert. Außerdem möchte ich dem Nutzer die Möglichkeit geben, bestimmte Werte eines Items zu verändern. Um das anzuzeigen, müsste ich ebenfalls einen neuen Adapter erzeugen. Das scheint mir viel zu kompliziert.

Ich bräuchte die Möglichkeit, wie bei einem ArrayAdapter, auf die Liste des Adapters zuzugreifen und dann per
Code:
notifyDataSetChange()
die Liste zu aktualisieren. Alternativ bräuchte ich einen ArrayAdapter, der mehr als ein Textview mit Inhalt füllt. Ich habe nur leider nichts derartiges gefunden.

Gibt es etwas, das mich näher ans Ziel führt oder bleibt mir nur, einen eigenen CustomArrayAdapter zu schreiben?

Gruß
 

dzim

Top Contributor
Hm... Ich hab jetzt ein bisschen den Überblick über das Problem verloren (zu viele andere in der Zeit gehabt :) ).
Noch mal kurz um mein Gedächtnis aufzufrischen: Verwendest du einen Loader? Wenn ja, dann kannst du in einer Methode den Loader einfach neustarten... [c]this.getLoaderManager().restartLoader(0x01, null, this);[/c] (this == ein Fragment).
Wen du es ohne Loader machst, bleibt dir gar nichts anderes übrig als den Adapter neu anzulegen und an die Liste zu hängen... Nennen wir es nicht dirty, sondern nur quick ;-)

Wobei eine Optimierung vielleicht noch ginge (aber vielleicht übersehe ich auch nur etwas): Verwende einen SimpleCursorAdapter als Basis und spare dir den Zwischenschritt über die Daten-Map - das reduziert schon mal die Zeit und den Speicherbedarf deiner Anwendung.
 

kurztipp

Aktives Mitglied
Verwendest du einen Loader?
Nein. Für den Loader brauche ich doch zwangsläufig einen ContentProvider oder? Und einen solchen zu erstellen erschien bisher zu unverhältnismäßig, zumal ich auch nicht weiß, wie groß der eventuelle Mehrwert ist. Müsste mal den Guide dazu lesen.

Wobei eine Optimierung vielleicht noch ginge (aber vielleicht übersehe ich auch nur etwas): Verwende einen SimpleCursorAdapter als Basis und spare dir den Zwischenschritt über die Daten-Map - das reduziert schon mal die Zeit und den Speicherbedarf deiner Anwendung.
Wenn ich einen SimpleCursorAdapter anlege, kann ich doch mit den Daten aus der Datenbank nicht mehr weiterarbeiten, oder? Der Ablauf aus ja: Daten anhand ID -> Berechnungen -> Mit den neuen Daten den neuen Adapter anlegen.

Wenn ich dazu einen CustomAdapter anlegen müsste, steh ich wieder vor dem Problem, dass ich nicht genau weiß, wie ich das anstelle ;)

[EDIT]Habe gerade entdeckt, dass das mit
Code:
getListView().getAdapter().getItem(index);
auf die einzelnen Maps zugegriffen werden kann. Das ist äußerst komfortabel.[/EDIT]

Gruß
 
Zuletzt bearbeitet:

dzim

Top Contributor
Hm. Ich hab gerade durch meinen Code geschaut und bin mir unsicher, ob man wirklich einen ContenProvider benötigt - er macht es sicher einfacher, aber du kannst einem SimpleCursorAdapter ja einen Cursor mitgeben - wie du den holst, ist im Endeffekt deine Sache - und in dem Fall kannst du auf den Loader auch verzichten, dann musst du nur jedes mal den Adapter austauschen, was sicher nicht so performant ist, wie mit Loader und ContentProvider...

Das mit dem
Code:
getItem(index)
hätte ich dir auch sagen können, wenn es mir spontan eingefallen wäre :)

Die Daten übrigens kannst du ja in jedem Fall immer über getItem() holen. Du könntest dann zur Laufzeit deinen Berechnungen den neuen holen.

Aber weißt du: Wenn du es erst einmal hast, baue es zu Ende. Ich habe auch nicht gleich beim ersten Mal die "beste" Lösung gehabt. Optimieren kannst du später immer noch! Ich lege dir, wenn du soweit bist, aber nahe, das Ganze mit einer ContentProvider-Implementierung aufzuwerten. Mehr bleibt erst einmal wohl nicht zu sagen, da du - zumindest vorübergehend - deine Lösung erst einmal gefunden zu haben scheinst.
 

Ähnliche Java Themen

Neue Themen


Oben