UI-Programmierung

jf

Bekanntes Mitglied
Hallo, bei Java-Komponenten hat man die add()-Methode - was ist das Äquivalent bei Android Views?
 

schlingel

Gesperrter Benutzer
Wie viel Zeit hast du denn investiert bevor du diese Frage gestellt hast? Gar keine?

Views können keine anderen Views hosten, das können nur ViewGroups (wie z.B. das LinearLayout). Nachschauen wie diese Methode heißt kann ich dir schon zumuten.
 

jf

Bekanntes Mitglied
Wie viel Zeit hast du denn investiert bevor du diese Frage gestellt hast? Gar keine?
Ich habe natürlich danach gegoogelt - aber für Android sieht es eben noch etwas mau aus...

Views können keine anderen Views hosten, das können nur ViewGroups (wie z.B. das LinearLayout). Nachschauen wie diese Methode heißt kann ich dir schon zumuten.
Ja, die Funktion einer ViewGroup wird in vielen Tutorials erwähnt - aber ich habe dies bisher stehts so verstanden, dass eine ViewGroup eine Art LayoutManager ist.
Ich will aber eine View erstellen - also eine richtige Komponente. Oder kann eine ViewGroup ebenfalls eine Komponente darstellen (und damit auch an alle Methoden übergeben werden, welche eine View als Parameter verlangen)?
 

schlingel

Gesperrter Benutzer
Klar, du kannst deine Klasse von ViewGroup oder einem anderen LayoutManager wie RelativeLayout, LinearLayout, etc. ableiten lassen und um die benötigten Methoden ergänzen. Und eine jede ViewGroup ist auch eine View, wie man in der Doku sieht.
 

jf

Bekanntes Mitglied
Klar, du kannst deine Klasse von ViewGroup oder einem anderen LayoutManager wie RelativeLayout, LinearLayout, etc. ableiten lassen und um die benötigten Methoden ergänzen. Und eine jede ViewGroup ist auch eine View, wie man in der Doku sieht.
Ok, danke.

LinearLayout stelle ich mir jetzt ähnich dem FlowLayout vor.
Welcher Layout-Manager unter Java kommt dem RelativeLayout am nächsten?

ViewGroup ist ja die Basis-Klasse von LinearLayout, funktionierte aber in meinem Test nicht:

Java:
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import android.widget.TextView;


public class MyViewGroup extends LinearLayout {

    public MyViewGroup(Context context) {
        super(context);
		TextView text = new TextView(context);
		text.setText("Ich bin ein Test!");
		this.addView(text);
    }

    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /*
    public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    */

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.e("SWIPED", "onLayout : " + Boolean.toString(changed));
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        super.onInterceptTouchEvent(event);
        Log.e("SWIPED", "onInterceptTouchEvent : " + event.getAction());
        return false;
    }
}

Wenn ich meine Klasse mit ViewGroup erweitere, dann wird die TextView nicht angezeigt.
Auch muss hier super.onLayout() auskommentiert werden. - Ist diese Methode letztendlich ausschlaggebend für die Darstellung?

Außerdem musste ich den Constructor mit defStyle auskommentieren - wird dieser nur unter neueren Android-Versionen unterstützt? (ich habe micht für das Android 2.1 SDK entschieden, da von 97% der Geräte unterstützt).

Vieleicht wäre auch die Frage ganz angebracht, ob es nicht schon eine Art "MenuView" gibt: ich möchte nämlich eine Menü-Struktur erzeugen (mit Untermenüs zu jedem einzelnen Menü-Eintrag).


PS:
Ich nehme an, dass die Klasse Log für ein Logging sorgt - wo finde ich die Ausgaben dazu?
(Sorry, bin noch ein absolutes Greenhorn!)
 

schlingel

Gesperrter Benutzer
Das hört sich sehr Desktop-lastig an, was ja im mobilen Umfeld gar nichts verloren hat. Vielleicht wäre da eine Liste mit einem Dialog-Menü für jedes Item besser geeignet oder den Quick-Actions.

Denn wenn du eine dynamische Liste hast, solltest du die auch als dynamische Liste anzeigen => ListView verwenden und nicht eine eigene View dafür schreiben.

Das onLayout setzt dir die Views dorthin wie es die ViewGroup oder du vorsiehst. LinearLayout ist kein FlowLayout da dieses automatisch umbringt. Wenn beim LinearLayout kein Platz mehr rechts ist, ist die View einfach außerhalb des Sichtfeldes. Außerdem kannst du dem LinearLayout auch sagen, es soll nach unten und nicht nach rechts wachsen.

Ohne Fehlermeldung weiß ich natürlich nicht was bei dem Code nicht stimmt aber so wie du deine Aufgabe beschreibst, solltest du das sowieso mit einer ListView und nicht einem eigenen View lösen.
 

jf

Bekanntes Mitglied
Das hört sich sehr Desktop-lastig an, was ja im mobilen Umfeld gar nichts verloren hat. Vielleicht wäre da eine Liste mit einem Dialog-Menü für jedes Item besser geeignet oder den Quick-Actions.
Die Quick-Actions sind ein super Tipp - allerdings werde ich um mehrere Untermenüs nicht herum kommen. Die Anwendung wäre wie folgt: ein Nutzer verwende die Applikation zur Protokollierung.

Bsp.:
Code:
Straße aaa
Straße bbb -> Objekt xxx
              Objekt yyy -> reparieren
                            austauschen
              Objekt zzz
Straße ccc

In dem Beispiel ist nur der Pfad eines Untermenüs dargestellt. In der Praxis werden den Menüs noch ein ganze Stück größer werden.
Ich dachte daher an ein Menü, wie es bei Apple üblich ist: wenn man einen Eintrag auswählt, schiebt es das Untermenü von rechts rein. Gibt es so etwas bei Android so etwas ähnliches - oder kann ich zumindest für das Hereinschieben folgendes nutzen: Link?

Die QuickActions können dann maximal den letzten Schritt darstellen. Da das letzte Menü zudem variabel ist und die Einträge nicht immer über Symbole verständlich dargestellt werden können, wäre noch zu fragen, ob man in den QuickActions auch Text unterbringen kann.
Für Standardmenüs, wie z. B. das Hinzufügen von Medien-Inhalten sind die QuickActions aber auf jeden Fall super! :)

Denn wenn du eine dynamische Liste hast, solltest du die auch als dynamische Liste anzeigen => ListView verwenden und nicht eine eigene View dafür schreiben.
Meine View sollte eigentlich das komplette Menü darstellen: mit dem Wechsel aller Untermenüs.

Das onLayout setzt dir die Views dorthin wie es die ViewGroup oder du vorsiehst.
Ok.

LinearLayout ist kein FlowLayout da dieses automatisch umbringt. Wenn beim LinearLayout kein Platz mehr rechts ist, ist die View einfach außerhalb des Sichtfeldes.
Ok.

Außerdem kannst du dem LinearLayout auch sagen, es soll nach unten und nicht nach rechts wachsen.
Ok, danke für den Tipp. - Ich hatte vermutet, dass dies bereits automatisch vertikal wächst, da dies auf Handys in der Regel am günstigsten ist.

Ohne Fehlermeldung weiß ich natürlich nicht was bei dem Code nicht stimmt aber so wie du deine Aufgabe beschreibst, solltest du das sowieso mit einer ListView und nicht einem eigenen View lösen.
Würdest du dies nach obiger Eläuterung immer noch raten?

Der Fehler wird beim Programmieren in der IDE angezeigt: remove argument to match ...
- Die Methode mit den 3 Argumenten scheint einfach nicht bekannt zu sein.
 

schlingel

Gesperrter Benutzer
So wie du es vorhast gibt's in Android nichts vorgefertigtes.

Du müsstest für jedes "Untermenü" eine eigene Activity erzeugen.

Das heißt du hast einmal eine Activity die dir alle Straßen auflistet. Klickst du drauf, kommst du weiter zur nächsten Activity die dir alle Objekte auflistest und wenn du dort draufklickst kommst du zu den Quick-Actions.

So nutzt du den beschränkten Platz am günstigsten aus und bleibst durch die Intents auch so generisch, dass du jeden der erzeugten Screens an jeder Stelle in deiner App aufrufen kannst.
 

jf

Bekanntes Mitglied
So wie du es vorhast gibt's in Android nichts vorgefertigtes.
Schade

Du müsstest für jedes "Untermenü" eine eigene Activity erzeugen.
Hmm. Kann man die Views nicht dynamsich ändern und so die Animation für die Untermenüs erzeugen?
Mir wäre ein Klasse, welche das komplette Menü erzeugt sehr viel lieber, da man diese dann einfach in anderen Projekten weiterverwenden kann. Außerdem hätte ich mir für das Endstadium eine Breadcrumb-Leiste gewünscht. -> Es würde die Anwendung selber daher wesentlich schlanker machen, wenn ich dort nur das Menü als einzelne Komopente einbinde und die Komponente selber in einem externen Projekt entwickle.

Das heißt du hast einmal eine Activity die dir alle Straßen auflistet. Klickst du drauf, kommst du weiter zur nächsten Activity die dir alle Objekte auflistest und wenn du dort draufklickst kommst du zu den Quick-Actions.
Würde das auch mit Animationen gehen?

So nutzt du den beschränkten Platz am günstigsten aus und bleibst durch die Intents auch so generisch, dass du jeden der erzeugten Screens an jeder Stelle in deiner App aufrufen kannst.
Das Anspringen eines jeden Menüs könnte man bei Bedarf auch bei einer einzelnen Komponente über eine spezielle Methode lösen.
 
Zuletzt bearbeitet:

schlingel

Gesperrter Benutzer
Es würde die Anwendung selber daher wesentlich schlanker machen, wenn ich dort nur das Menü als einzelne Komopente einbinde und die Komponente selber in einem externen Projekt entwickle.
Genau das Gegenteil ist der Fall. Die eine Activity mit den vielen Anzeigen würde diesen Code wahnsinnig aufblähen und verkomplizieren. Das würde zu einem reinen Alptraum führen.
Das führt mich zum nächsten:

Kann man die Views nicht dynamsich ändern und so die Animation für die Untermenüs erzeugen?
Kann man, soll man aber nicht. Operationen die am View-Baum durchgeführt werden sind wahnsinnig teuer.

Würde das auch mit Animationen gehen?
Jein, denn die kommt vom System. Oder du machst das per ViewFlipper, siehe auch weiter unten.

Das Anspringen eines jeden Menüs könnte man bei Bedarf auch bei einer einzelnen Komponente über eine spezielle Methode lösen.
Schau dir lieber mal den App-Lifecycle von Android an und lies dich in die Intent-Philosophie ein. Du versuchst hier Swing-Pattern reinzubringen und da haben sie nichts verloren. Z.B. Breadcrumbs wären auch über mehrere Activities verteilt leicht möglich. Wenn du das an deine View bindest und den Screen wechselst ist diese Funktionalität nämlich nicht mehr erreichbar.

Eine andere Möglichkeit die etwas in die Richtung geht in der du das haben möchtest wäre es, wenn du es mit einem ViewFlipper löst. Der enthält dann die jeweiligen Seiten die du anzeigen möchtest und läßt dich zwischen den Screens hin und her "flippen".

Allerdings müsstest du auch dann wieder etwas selber basteln damit die Views dynamisch befüllt werden mit den gerade aktuellen Unterseiten.
 

jf

Bekanntes Mitglied
Genau das Gegenteil ist der Fall. Die eine Activity mit den vielen Anzeigen würde diesen Code wahnsinnig aufblähen und verkomplizieren. Das würde zu einem reinen Alptraum führen.
Naja, ich wollte das Menü aus einer XML-Datei lesen und dann daraus völlig generisch das Menü aufbauen: hier gäbe es dann nur eine Klasse MenuPage und eine Instanz davon für jedes Submenü. - Mit einer guten Architektur sollt das nicht schlimm werden.

Kann man, soll man aber nicht. Operationen die am View-Baum durchgeführt werden sind wahnsinnig teuer.
Soll heißen, es wird keine Untermenü-Animation, sondern eher eine Untermenü-Ruckelaktion?

Schau dir lieber mal den App-Lifecycle von Android an und lies dich in die Intent-Philosophie ein.
Ich versuche, was ich kann - leider ist die Zeit für eine Intensive Recherche viel zu knapp. :(

Du versuchst hier Swing-Pattern reinzubringen und da haben sie nichts verloren.
Ja, Swing ist das was ich kenne und auch recht gut finde.

Z.B. Breadcrumbs wären auch über mehrere Activities verteilt leicht möglich. Wenn du das an deine View bindest und den Screen wechselst ist diese Funktionalität nämlich nicht mehr erreichbar.
Die View würde ja dann das komplette Menü darstellen - damit hätten die Breadcrumbs auf anderen Screens eh nichts verloren.

Eine andere Möglichkeit die etwas in die Richtung geht in der du das haben möchtest wäre es, wenn du es mit einem ViewFlipper löst. Der enthält dann die jeweiligen Seiten die du anzeigen möchtest und läßt dich zwischen den Screens hin und her "flippen".

Allerdings müsstest du auch dann wieder etwas selber basteln damit die Views dynamisch befüllt werden mit den gerade aktuellen Unterseiten.
Ok, das klingt gut. Ich werde es mir mal anschauen.


PS:
Ich habe nun schon mal etwas mit der ListView rumgespielt: hier gibt es ja die Methode getItemAtPosition - aber ich vermisse eine Methode setItem... oder addItem...
Hier wird zwar angegeben, dass dies über ein String-Array passiert - aber in der Doku habe ich keine Methode gefunden, welche einen Parameter String[] erwartet. Wie bekommt man nun die Daten in die ListView?
 

schlingel

Gesperrter Benutzer
Generisch ein Menü aufbauen hat seine Grenzen. Irgendwo musst du eine Form vorgeben.

Und die beste Architektur schützt dich nicht davor, wenn du gegen die bestehende arbeitest. Dann ist Murks im wahrsten Sinne des Wortes vorprogrammiert. Und es hilft auch nichts, dass du Swing gut kannst - ich finde Swing für den Desktop auch gut - aber die Pattern darfst du nicht 1:1 auf andere Plattformen übertragen.

Es gibt zwar keine Android-Polizei die dir auf den Kopf haut, aber der Code wird dir auf den Kopf fallen ;-) Dir würde ja auch nicht einfallen, nur weil es in JavaScript Gang und Gebe ist - in Java per Reflection deinen Objekten Methoden und Attribute hinzuzufügen. Klar gibt's Wege dafür, aber das will niemand so machen.

Soll heißen, es wird keine Untermenü-Animation, sondern eher eine Untermenü-Ruckelaktion?
Darf man erwarten. So wie du es erzählst hättest du dann viele dutzend Views und dann hat der GC schon einiges zu tun. Im schlimmsten Fall hältst du zu viele Views in deinen Listen und bekommst einen OutOfMemoryError => App tot.

Punkto ListView: Ja das ist tricky, da bin ich auch etwas gehangen bevor ich's begriffen habe. Die ListView ist eine reine View. Alle deine Daten kommen vom Controller bzw. in Android auch ListAdapter genannt. Als Basisklasse für deine Listen gibt's die ArrayAdapter<DeinModelTyp> und die BaseAdapter<DeinModelTyp>.

Ich leite immer von ArrayAdapter ab. Dort fügst du dann die Methoden die dir fehlen hinzu und gut ist. Nicht vergessen die getView-Methode zu überschreiben, die die einzelnen ListView-Elemente befüllt. Hier habe ich ein einfaches Beispiel für dich.
 

jf

Bekanntes Mitglied
Generisch ein Menü aufbauen hat seine Grenzen. Irgendwo musst du eine Form vorgeben.

Und die beste Architektur schützt dich nicht davor, wenn du gegen die bestehende arbeitest. Dann ist Murks im wahrsten Sinne des Wortes vorprogrammiert. Und es hilft auch nichts, dass du Swing gut kannst - ich finde Swing für den Desktop auch gut - aber die Pattern darfst du nicht 1:1 auf andere Plattformen übertragen.
Da gehe ich mit. - Aber ich finde es eben eine gute Phílosophie, wenn man versucht modular zu arbeiten und zusammenhängende Dinge in eine Komponente zu stecken, welche dann einfach einzusetzen ist, ohne das man groß Wissen über die internen Details benötigt. - Dies hilft hinsichtlich code reuse.
Ich werde es daher trotzdem mal mit dem ViewFlipper versuchen - mal schauen, wie weit ich komme, ohne zu viel Zeit dafür zu verschwenden...

Es gibt zwar keine Android-Polizei die dir auf den Kopf haut, aber der Code wird dir auf den Kopf fallen ;-) Dir würde ja auch nicht einfallen, nur weil es in JavaScript Gang und Gebe ist - in Java per Reflection deinen Objekten Methoden und Attribute hinzuzufügen. Klar gibt's Wege dafür, aber das will niemand so machen.
Nein, dass will man nicht. Dem stimme ich zu. - Ich finde aber auch JavaScript nicht so toll...

Darf man erwarten. So wie du es erzählst hättest du dann viele dutzend Views und dann hat der GC schon einiges zu tun. Im schlimmsten Fall hältst du zu viele Views in deinen Listen und bekommst einen OutOfMemoryError => App tot.
Naja, die Menüs bestehen ja nur aus ein paar Strings. - Nehmen wir mal insgesamt 1000 Strings mit maximal 50 Zeichen als Obergrenze an, dann wären wir bei gerade mal 100kB an Daten. Und der GC sollte doch mit 20...50 Objekten klar kommen.
Ich hätte da jetzt mehr bedenken bei den Animationen - die sind sicher Hardwarebeschleunigt.
Wenn ich jetzt einfach bloß versuche die Views absolut hin- und herzuschieben, könnte es bei einem ARM evtl. knapp werden. (Zumal ich keine Ahnung habe, wie man das unter Android überhaupt machen kann - bei Swing muss man es ja mit invokeLater() im EDT machen.)

Punkto ListView: Ja das ist tricky, da bin ich auch etwas gehangen bevor ich's begriffen habe. Die ListView ist eine reine View. Alle deine Daten kommen vom Controller bzw. in Android auch ListAdapter genannt. Als Basisklasse für deine Listen gibt's die ArrayAdapter<DeinModelTyp> und die BaseAdapter<DeinModelTyp>.

Ich leite immer von ArrayAdapter ab. Dort fügst du dann die Methoden die dir fehlen hinzu und gut ist. Nicht vergessen die getView-Methode zu überschreiben, die die einzelnen ListView-Elemente befüllt. Hier habe ich ein einfaches Beispiel für dich.
Vielen Dank für das Beispiel!
Ich habe es an mein Test-Projekt angepasst (ich arbeite ja ohne XML-Layout): habe sogar die Exceptions weg bekommen - aber es werden auch keine Daten angezeigt. Es entsteht nur ein Rechteckt, welches man erst sieht, wenn man den AVD aus einem sehr spitzen Winkel betrachtet.
Ich bin bei dem Prinzip auch noch nicht ganz durchgestiegen: Warum muss ich im Adapter TextViews anlegen? - Werden dann immer jeweils zwei Labels pro Listeneintrag erstellt? Und ist der Preis dann auch automatisch rechtsbündig?

Ich habe es wie folgt geändert:

Java:
package common.view.menu;


import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;


public class ItemAdapter extends ArrayAdapter<String> {
	private List<String> items;
	private LayoutInflater inflater;
	private Context m_context = null; 

	public ItemAdapter(Context context, int textViewResourceId,	List<String> objects) {
		super(context, textViewResourceId, objects);
		this.m_context = context;
		this.items = objects;
		this.inflater = LayoutInflater.from(context);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView == null) {
			convertView = new View(m_context); // inflater.inflate(R.layout.purchase_list_item, null);
		}

		final TextView lblName = new TextView(m_context); //(TextView)convertView.findViewById(R.id.lblName);
		final TextView lblPrice = new TextView(m_context); //(TextView)convertView.findViewById(R.id.lblPrice);
		final String item = getItem(position);

		lblName.setText(item.toString());
		lblPrice.setText(getPriceTag(item));

		return convertView;
	}

	private String getPriceTag(final String item) {
		return item; //Utils.format(getContext(), R.string.fmtStrPrice, (float)item.getPriceInCent() / 100f);
	}

	@Override
	public int getCount() {
		return items.size();
	}

	@Override
	public void add(String object) {
		items.add(object);
	}

	@Override
	public String getItem(int position) {
		return items.get(position);
	}

	@Override
	public void clear() {
		items.clear();
	}

	public void addItemAt(String item, int pos) {
		items.add(pos, item);
	}

	public List<String> getAllItems() {
		return items;
	}

}


und das ist meine TestView, welche die Liste enthalten soll:

Java:
package common.view.menu;


import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class MenuView extends LinearLayout {
	  static final String[] COUNTRIES = new String[] {
		    "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
		    "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
		    "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
		    "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
		    "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
		    "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
		    "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
		    "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
		    "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
		    "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
		    "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
		    "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
		    "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
		    "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
		    "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
		    "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
		    "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
		    "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
		    "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
		    "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
		    "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
		    "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
		    "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
		    "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
		    "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
		    "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
		    "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
		    "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
		    "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
		    "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
		    "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
		    "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
		    "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
		    "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
		    "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
		    "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
		    "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
		    "Ukraine", "United Arab Emirates", "United Kingdom",
		    "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
		    "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
		    "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
	};
	  
	public MenuView(final Context context) {
		super(context);

		TextView text = new TextView(context);
		text.setText("Root!");
		this.addView(text);

		List<String> countries = new ArrayList<String>();
		
		for(String country : COUNTRIES) {			
			countries.add(country);
		}
		
		ListView list = new ListView(context);
		list.setAdapter( (ListAdapter)new ItemAdapter(context, 0, countries) );
		
		list.setOnItemClickListener(new OnItemClickListener() {
			// When clicked, show a toast with the TextView text
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
	    		Toast.makeText(context, ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
	    	}
		});

		this.addView(list);		
	}

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.e("SWIPED", "onLayout : " + Boolean.toString(changed));
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        super.onInterceptTouchEvent(event);
        Log.e("SWIPED", "onInterceptTouchEvent : " + event.getAction());
        return false;
    }

}

Könntest du mir bitte doch noch einmal unter die Arme greifen? Vielen Dank.
 
Zuletzt bearbeitet:

schlingel

Gesperrter Benutzer
Viel modularer als mit Intents kann man nicht mehr arbeiten - da koppelst du so lose, dass du nicht einmal mehr ein Interface brauchst. Nimm dir die 30min und lies dir durch was das ist und wie es funktioniert.

Bei den Menüs geht's ja nicht um die Strings sondern um die Views die sie darstellen. Von Google gibt es die Empfehlung nie mehr als 34 Views auf einer Seite zu verwenden, mit dem Hinweis wenn es mehr sind diese in eine ListView zu geben. Zusätzlich kommen dann ja auch noch die vielen, vielen Objekte die du gar nicht siehst als Programmierer.
Der Quellcode ist ja offen, schau mal rein - ich hab sehr gestaunt wofür da überall Objekte angelegt werden.
Animation kommt dann auch noch dazu.

Wenn du dir deinen Code folgendermaßen vorstellst, dein convertView ist deine Pane, die anderen Views sind deine JTextField etc. Dann würde Swing auch nichts anzeigen wenn du deine Controls der Pane nicht hinzufügst.

getView macht ja nix anderes als die View zurückzuliefern die für das Element an der angefragten Position angezeigt werden soll. Die Überprüfung ob es null ist macht man um sich die teure Objekterzeugung zu sparen.

Das heißt die convertView muss eine ViewGroup sein, die deine Elemente enthält. Die holst du dir im Normalfall mit
Java:
TextEdit txtBxFeld1 = (TextEdit)convertView.findViewById(R.id.deineId);
heraus. An dieser Stelle möchte ich stark davon abraten in Android alles per Java-Code anstatt per XML-Code zu erzeugen. Das macht wenig Sinn, da das ganze UI-System darauf ausgelegt ist.

Ich kann's nur immer wieder wiederholen: Tu dir den Gefallen und schau dir an wie man richtig im Android-Framework programmiert. Eine jede "Zeitersparnis" die so brutal die Richtlinien ignoriert wird dich bei fortgeschrittenem Projekt 10 mal mehr Zeit kosten.
Ich sag das nicht für mich - ich muss es ja nicht warten :p
 

jf

Bekanntes Mitglied
Das heißt die convertView muss eine ViewGroup sein, die deine Elemente enthält.
Ok, aber durch das Überschreiben von getView bin ich doch auf View festgelegt.
Wenn ich folgendes einfüge, kommt es daher zu einer ClassCastException (kann nicht View nach ViewGroup casten):
Java:
		((ViewGroup) convertView).addView(lblName);
		((ViewGroup) convertView).addView(lblPrice);

An dieser Stelle möchte ich stark davon abraten in Android alles per Java-Code anstatt per XML-Code zu erzeugen. Das macht wenig Sinn, da das ganze UI-System darauf ausgelegt ist.
Ja, leider. - Aber überall wurde auch gesagt, dass man das UI auch in Java schreiben kann, auch wenn es nicht die bevorzugte Variante ist.

Ich kann's nur immer wieder wiederholen: Tu dir den Gefallen und schau dir an wie man richtig im Android-Framework programmiert. Eine jede "Zeitersparnis" die so brutal die Richtlinien ignoriert wird dich bei fortgeschrittenem Projekt 10 mal mehr Zeit kosten.
Ja, ich habe auch schon damit angefangen - nur leider hat mich auch schon recht vieles dabei sehr enttäsucht... :(
 

schlingel

Gesperrter Benutzer
Ja ist denn deine convertView eine ViewGroup? Wenn du den Code gleich gelassen hast und nur die zwei Zeilen hinzugefügt hast, ist es ja nach wie vor eine View und keine ViewGroup.
Du musst dir im Code eine ViewGroup erstellen, am besten ein LinearLayout oder sonst irgendetwas einfaches und dort dann die Elemente hinzufügen.

Glaub mir, wenn du am falschen Prinzip fest hältst wird's nur noch mehr Enttäuschungen geben. Das Framework ist teilweise noch ziemlich crappy - ich verwende es ziemlich viel und traue mich das sagen - aber Workarounds gehören wirklich nur dorthin wo es was zum umgehen gibt.
 

jf

Bekanntes Mitglied
Ja ist denn deine convertView eine ViewGroup? Wenn du den Code gleich gelassen hast und nur die zwei Zeilen hinzugefügt hast, ist es ja nach wie vor eine View und keine ViewGroup.
Das ist korrekt. - Aber folgende Deklaration schreibt ja View als Typ vor:
Code:
    public View getView(int position, View convertView, ViewGroup parent) {

Du musst dir im Code eine ViewGroup erstellen, am besten ein LinearLayout oder sonst irgendetwas einfaches und dort dann die Elemente hinzufügen.
Muss ich dieser ViewGroup dann auch die convertView (Typ View) hinzufügen?
Was soll das bringen. - Ich muss doch die die View zurückgeben, den Listeneintrag darstellt, oder nicht?
Ich könnte mal versuchen einfach nur eine der TextViews zurückzugeben anstatt der convertView...

Glaub mir, wenn du am falschen Prinzip fest hältst wird's nur noch mehr Enttäuschungen geben. Das Framework ist teilweise noch ziemlich crappy - ich verwende es ziemlich viel und traue mich das sagen - aber Workarounds gehören wirklich nur dorthin wo es was zum umgehen gibt.
Ich glaube dir ja. Nur habe ich auch meine Philosophie und auch etwas Ehrgeiz, weshalb auch auch gerne mal gegen den Strom schwimme. :)
Es muss doch möglich sein, eine ListView nur in Java hinzubekommen - alles andere wäre ein extremes versagen seitens Google! :autsch:
 

schlingel

Gesperrter Benutzer
Bevor du da Google den schwarzen Peter zuschieben magst, schau dir noch mal diesen Thread an. ;-)

Wie weiter oben geschrieben, ist eine ViewGroup auch eine View. Du musst also nicht deiner ViewGroup die convertView hinzufügen (das wäre tatsächlich sinnlos), du sollst deine convertView als ViewGroup instanzieren. Wie heißt's in der ersten Stunde einer fast jeden OOP-Vorlesung: Die Klasse Elefant erbt von Säugetier und kann also auch an jeder Stelle verwendet werden wo die Klasse Säugetier erwartet wird.

Das mit der Philosophie ist so eine Sache. Solange du alleine arbeitest ist es ja OK, aber sobald andere in's Spiel kommen (können), sollte man sich doch an Best Practices halten. Ein Projekt bietet ja genug Chancen trotzdem einen schwer zu verstehenden Code zu bauen :-D
 

jf

Bekanntes Mitglied
Bevor du da Google den schwarzen Peter zuschieben magst, schau dir noch mal diesen Thread an. ;-)
An dem ist doch auch Google schuld! :bae:

Ist auch so eine Philosophie von mir:
Alles sollte möglichst so intuitiv sein, dass man mit trial and error selber weiterkommt. Dann ist auch eine Doku nicht mehr so wichtig. Viel besser sind meines Erachtes Kurzbeispiele für alle möglichen kniffligeren Dinge. Die Lernkurve sollte eben nicht allzu steil sein, dann macht die Sache einfach viel mehr Spass! :D

Wie weiter oben geschrieben, ist eine ViewGroup auch eine View. Du musst also nicht deiner ViewGroup die convertView hinzufügen (das wäre tatsächlich sinnlos), du sollst deine convertView als ViewGroup instanzieren.
Ok, aber ViewGroup ließ sich nicht instanzieren, habe daher das LinearLayout verwendet.
Dies verbot sich mir eigentlich von selbst, da die Instanzierung in einer If-Bedingung eingefasst war:
sollte convertView mal nicht null sein, dann würde nämlich auch die ClassCastException kommen.
- Oder wird convertView nur einmal am Anfang instanziert und anschießend genau dieses Objekt immer wieder der getView()-Methode mitgegeben?

Wie heißt's in der ersten Stunde einer fast jeden OOP-Vorlesung: Die Klasse Elefant erbt von Säugetier und kann also auch an jeder Stelle verwendet werden wo die Klasse Säugetier erwartet wird.
Ok, ich hatte nie solch eine Vorlesung. Das Problem war wohl eher, dass bei mir die ViewGroup noch irgendwo in der Ecke "LayoutManager" lag - und ich somit die Tatsache ignorierte, dass ViewGroup auch eine View (also Ecke "Component") ist.

Das mit der Philosophie ist so eine Sache. Solange du alleine arbeitest ist es ja OK, aber sobald andere in's Spiel kommen (können), sollte man sich doch an Best Practices halten. Ein Projekt bietet ja genug Chancen trotzdem einen schwer zu verstehenden Code zu bauen :-D
Aber auch genügend, einen gut zu verstehenden zu bauen.
- Und das muss nicht zwangsweise Best Practices sein. ;)

Ok, die Liste funktioniert jetzt - dafür bekommst du nun ein dickes Danke! :toll:
 
Zuletzt bearbeitet:

schlingel

Gesperrter Benutzer
sollte convertView mal nicht null sein, dann würde nämlich auch die ClassCastException kommen.
- Oder wird convertView nur einmal am Anfang instanziert und anschießend genau dieses Objekt immer wieder der getView()-Methode mitgegeben?
Jepp, außer dir instanziert niemand die Elemente. Du bist ja im Adapter, der erledigt den Job. Das if spart dir das Instanzieren, was ja sehr teuer ist.
Prinzipiell hier ein Tipp von mir: Schau dir an wie der Dalvik GC arbeitet und wie man Apps optimiert. Normalerweise sagt man immer premature optimization is the root of all evil aber auf einem Microcontroller würdest du auch nicht wie auf einem Linux PC C programmieren.
Die Grenzen sollte man schon kennen. Desktop-Entwicklung ist nur begrenzt auf mobile Entwicklung übertragbar.

Aber auch genügend, einen gut zu verstehenden zu bauen. - Und das muss nicht zwangsweise Best Practices sein.
Ist ja deine Sache, aber nicht vergessen: Software-Entwicklung und Software-Planung ist eine Ingenieurstätigkeit. Zwar eine sehr junge der noch viel im Bereich der wissenschaftlich abgesicherten Methodologie fehlt, aber doch so weit, dass man sich auf ein paar Dinge einigen konnte. Dazu gehören die Best Practices für verschiedene Sprachen und Frameworks, weil so neue Mitarbeiter schneller in die Materie reinfinden.
Einem europäischen Tiefbau-Ingenieur würde ja auch nicht einfallen in Pfund und Fuß anstatt in kg und Meter zu rechnen, weil er das so im Studium in den Staaten gelernt hat. Das würde seinem europäischen Team ganz schön zu schaffen machen.
 

jf

Bekanntes Mitglied
Prinzipiell hier ein Tipp von mir: Schau dir an wie der Dalvik GC arbeitet und wie man Apps optimiert.
Ja, die OOP verleitet schon mal dazu alles und jedes schön in einem Objekt zu kapseln - wenn dann aber sehr viele davon erzeugt werden, kostet das zusätzliche Ressourcen gegenüber einem prozeduralem Ansatz. Dem muss man sich natürlich im Klaren sein.

Normalerweise sagt man immer premature optimization is the root of all evil aber auf einem Microcontroller würdest du auch nicht wie auf einem Linux PC C programmieren.
Die Grenzen sollte man schon kennen. Desktop-Entwicklung ist nur begrenzt auf mobile Entwicklung übertragbar.
Ja, dies ist die goldene Regel der Programmierung! :D
Jede Optimierung, welche den Code umfangreicher und damit unleserlicher macht und am Schluß keinen Unterschied bewirkt ist absolut unötig.
In diesem Fall würde ich aber noch nicht von Optimierung sprechen, denn jedes unötig erzeugte Objekt ist meines Erachtens nach hässlich.
Die
Code:
if(convertView == null)
macht den Code ja auch nicht unleserlicher - im Gegenteil: es hat dabei geholfen zu erkennen, dass das erstellte Objekt beim nächsten Aufruf der Routine wieder mitgegeben wird.

Ist ja deine Sache, aber nicht vergessen: Software-Entwicklung und Software-Planung ist eine Ingenieurstätigkeit. Zwar eine sehr junge der noch viel im Bereich der wissenschaftlich abgesicherten Methodologie fehlt, aber doch so weit, dass man sich auf ein paar Dinge einigen konnte. Dazu gehören die Best Practices für verschiedene Sprachen und Frameworks, weil so neue Mitarbeiter schneller in die Materie reinfinden.
Das hättest du mal unseren Vorgängern hier sagen sollen! :lol:
Niemand von denen mehr da, keine Doku und der Code das reinste Chaos:
  • globale Variablen en masse
  • gleich mal 15 davon nur für die (eine!) Uhrzeit
  • uninitialisierte Variablen - und das in C!
  • hier und da Endlosschleifen
  • der komplette Code durchzogen mit alternierenden Präprozessor-Switches
  • keine sinnvolle Kommentierung
  • und noch viele viele solcher Dinge mehr...
Glaube mir, ich lege sehr viel Wert auf sauberen Code - in welcher Form das passiert ist aber eben wohl doch etwas Geschmackssache.

Einem europäischen Tiefbau-Ingenieur würde ja auch nicht einfallen in Pfund und Fuß anstatt in kg und Meter zu rechnen, weil er das so im Studium in den Staaten gelernt hat. Das würde seinem europäischen Team ganz schön zu schaffen machen.
Ich würde sogar sagen, dass es unsauber ist, überhaupt in Pfund und Fuß zu programmieren - maximal bei der Präsentation für den Nutzer dürfen die Werte umgerechnet, aber keines Falls intern so verwendet werden!
Durch einen Fehler bei einer solchen Einheiten-Geschichte ist sogar schon mal ein Millionen-teuer Mars-Satellite abgestürzt...

Ich habe jetzt eine ListViewEx-Klasse erstellt, welche mir die Arbeit mit einem ListView vereinfacht. Ex steht dabei für Extendet, dass habe ich so von der Windows-API übernommen. Ich möchte dir hier kurz zeigen, wie ich das mit der Komponenten-Philosophie meine - du kannst dann ja für dich entscheiden, ob es vielleicht doch ok ist, oder eben absolut nicht dein Fall. :)

In ein Package eines Android-Library-Projektes habe ich folgende zwei Klassen gepackt:
Java:
package common.view.list;


import java.util.List;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;


public class ItemAdapter extends ArrayAdapter<Object> {
	private Context      m_context = null;
	private List<Object> m_items   = null;

	
	// constructors //////////////////////////////////////////////////////////////
	
	public ItemAdapter(Context context, List<Object> objects) {
		super(context, 0, objects);
		m_context = context;
		m_items   = objects;
	}
	
	public ItemAdapter(Context context, Object[] objects) {
		super(context, 0, objects);
		m_context = context;
		
		for(Object item : objects) {
			m_items.add(item);
		}
	}

	
	// override methods //////////////////////////////////////////////////////////
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView == null) {
			convertView = new LinearLayout(m_context);
		}

		final TextView lblName = new TextView(m_context);
		final Object item = this.getItem(position);
		
		lblName.setText(item.toString());
		((LinearLayout)convertView).addView(lblName);

		return convertView;
	}

	@Override
	public int getCount() {
		return m_items.size();
	}

	@Override
	public Object getItem(int position) {
		return m_items.get(position);
	}
	
	@Override
	public void clear() {
		m_items.clear();
	}
	
	@Override
	public void add(Object object) {
		m_items.add(object);
	}

	
	// other methods /////////////////////////////////////////////////////////////
	
	public void addItemAt(Object item, int pos) {
		m_items.add(pos, item);
	}
	
	public void addItems(Object[] items) {
		for(Object item : items) {
			this.add(item);
		}
	}

	public void addItems(List<Object> list) {
		for(Object item : list) {
			this.add(item);
		}
	}

	public void addItemsAt(Object[] items, int pos) {
		for(Object item : items) {
			this.addItemAt(item, pos++);
		}
	}

	public void addItemsAt(List<Object> list, int pos) {
		for(Object item : list) {
			this.addItemAt(item, pos++);
		}
	}

	public void setItems(Object[] items) {
		m_items.clear();

		for(Object item : items) {
			this.add(item);
		}
	}

	public void setItems(List<Object> list) {
		m_items.clear();

		for(Object item : list) {
			this.add(item);
		}
	}

	public List<Object> getItems() {
		return m_items;
	}
	
}

Java:
package common.view.list;


import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListAdapter;
import android.widget.ListView;


public class ListViewEx extends ListView {

	// constructors //////////////////////////////////////////////////////////////
	
	public ListViewEx(Context context) {
		super(context);
		this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<Object>()) );
	}
	public ListViewEx(Context context, AttributeSet attrs) {
		super(context, attrs);
		this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<Object>()) );
	}
	public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<Object>()) );
	}

	
	// methods ///////////////////////////////////////////////////////////////////

	public void addItem(Object item) {
		((ItemAdapter)this.getAdapter()).add(item);
	}

	public void addItemAt(Object item, int pos) {
		((ItemAdapter)this.getAdapter()).addItemAt(item, pos);
	}

	public void addItems(Object[] items) {
		((ItemAdapter)this.getAdapter()).addItems(items);
	}

	public void addItemsAt(Object[] items, int pos) {
		((ItemAdapter)this.getAdapter()).addItemsAt(items, pos);
	}

	public void addItems(List<Object> list) {
		((ItemAdapter)this.getAdapter()).addItems(list);
	}

	public void addItemsAt(List<Object> list, int pos) {
		((ItemAdapter)this.getAdapter()).addItemsAt(list, pos);
	}

	public void setItems(Object[] array) {
		((ItemAdapter)this.getAdapter()).setItems(array);
	}
	
	public void setData(List<Object> list) {
		((ItemAdapter)this.getAdapter()).setItems(list);
	}

}

Nun binde ich nur noch das Library-Projekt in mein aktuells Android-Projekt ein und kann mit folgenden zwei Zeilen eine ListView anzeigen:
Java:
		ListViewEx list = new ListViewEx(context);
		list.addItems(COUNTRIES);

Dies hat folgende Vorteile:
  • das Hauptprojekt wird schank gehalten und bleibt dadruch übersichtlicher
  • die zwei Zeilen sind schneller geschrieben, als eine Adapter-Klasse hinzugefügt
  • der Anwender der Klasse muss nichts über den intern verwendeten Adapter wissen - er kann einfach über das Intellisense der ListView-Instanz die passende Methode wählen, ohne auch nur eine Google-Suche ausführen zu müssen. Eine Diskussion wie diese wäre überflüssig. => es ist einfach intuitiver!

Man könnte anstatt ListView zu erweitern das Problem auch mittels Komposition lösen, um die Intellisense-Liste sauber zu halten. - In diesem Fall wird man aber in Spezialfällen sicherlich einige Methoden vermissen und muss die ListViewEx dann ggf. erweitern. Mit Vererbung bleibt man flexibler, auch wenn es nicht ganz so sauber ist. So könnte ich z. B. immer noch einen eigenen Adapter verwenden, wenn ich es will.
Evtl. kann man den Adapter noch so erweitern, dass er mehrere Spalten erzeugt, wenn in den einzelnen Items wiederum Arrays stecken. Dies würde aber einige Arbeit machen, da man ja auch den Fall abfangen sollte, wenn der Nutzer unterschiedlich lange Arrays für die Items übergibt - daher habe ich mir das jetzt mal gespart. Dies würde dann der Funktion eines CellRenderers unter Java nahe kommen.

Wenn man dann eine kleine Doku zur Libarary macht und zu jedem Hauptprojekt angibt, was aus der Library verwendet wird, dann sollte jeder damit gut klar kommen - Android-Neulinge vlt. sogar besser, als mit Best Practice.

Noch "ein" Wort zum XML-Layouting: damit komme ich nun überhaupt nicht klar!
  • man muss hier nur um einen String zu ändern in eine andere Eclispe-View wechseln, was einige Klicks erfordert
  • die XML-Programmierung ist mit seinen öffnenden und schließenden Tags sehr lässtig - Java-Syntax liest sich schöner und ist dazu schneller und einfacher geschrieben.
  • die Ressourcen für eine Komponente gehören zur Komponente dazu - sie sollten daher auch am gleichen Ort abgelegt werden (Komponenten-Philosophie eben). Für die autoamtische Generierung der R-Klasse ist aber ein separter Ressourcen-Ordner nötig.
=> Es streubt sich einfach alles in mir, das XML-Layouting anzunehmen.
Das ist wohl so wie mit der Relligion: man muss die Ansichten anderer einfach tolerieren.

Ich lasse mir gern vielen sagen, aber nicht, dass ich unstrukturiert und unsauber programmiere. ;)
Letztenlich muss man einfach mit seinem eigenen Stil klar kommen. Wobei ich bei Android natürlich auch noch am Lernen bin.
- Es kann also gut sein, dass ich in Zukunft leider noch die ein oder andere Diskussion eröffnen muss... :oops:

Nochmal vielen Dank für deine Hilfe und auch für die Tipps bzgl. Android-Programmierung. Ich werde diese bei meinen weiteren Erfahrungen mit Android beachten.
 
Zuletzt bearbeitet:

schlingel

Gesperrter Benutzer
Ich lasse mir gern vielen sagen, aber nicht, dass ich unstrukturiert und unsauber programmiere.
Sorry wenn du das so verstanden hast, das wollte ich nicht sagen. Die Software entwerfen und programmieren sind zwei verschiedene Dinge, so wie es Taktik und Strategie sind.

Strukturiert und sauber programmieren hilft alles nichts wenn die Architektur inhärent gegen Android-Designrichtlinien verstößt. Wie im Gefecht: Du kannst der beste Taktiker sein, aber wenn die Strategie nicht passt wird dir der Feind trotzdem den Nachschub abgraben.

der komplette Code durchzogen mit alternierenden Präprozessor-Switches
I feel your pain - das hasse ich auch wie die Pest.

Und abschließend noch etwas: Ab Java 5 solltest du die Generics verwenden. Also ca. so:

Java:
public class ListViewEx<T> extends ListView {
 
    // constructors //////////////////////////////////////////////////////////////
    
    public ListViewEx(Context context) {
        super(context);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<T>()) );
    }
    public ListViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<T>()) );
    }
    public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<Object>()) );
    }
 
    
    // methods ///////////////////////////////////////////////////////////////////
 
    public void addItem(T item) {
        ((ItemAdapter)this.getAdapter()).add(item);
    }
 
    public void addItemAt(T item, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemAt(item, pos);
    }
 
    public void addItems(T[] items) {
        ((ItemAdapter)this.getAdapter()).addItems(items);
    }
 
    public void addItemsAt(Object[] items, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemsAt(items, pos);
    }
 
    public void addItems(List<T> list) {
        ((ItemAdapter)this.getAdapter()).addItems(list);
    }
 
    public void addItemsAt(List<T> list, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemsAt(list, pos);
    }
 
    public void setItems(T[] array) {
        ((ItemAdapter)this.getAdapter()).setItems(array);
    }
    
    public void setData(List<T> list) {
        ((ItemAdapter)this.getAdapter()).setItems(list);
    }
 
}

Bzgl. Laufzeit egal, gibt dir aber Typsicherheit zur Compile-Zeit. Und punkto Typinferenz hättest du mit List<Object> ja auch nicht mehr Vorteile als mit List<T>.
 

jf

Bekanntes Mitglied
Sorry wenn du das so verstanden hast, das wollte ich nicht sagen.
Nein, so kam es nicht rüber - ich wollte nur etwas meinen Programmier-Stil verteitigen. :)

Die Software entwerfen und programmieren sind zwei verschiedene Dinge, so wie es Taktik und Strategie sind.

Strukturiert und sauber programmieren hilft alles nichts wenn die Architektur inhärent gegen Android-Designrichtlinien verstößt. Wie im Gefecht: Du kannst der beste Taktiker sein, aber wenn die Strategie nicht passt wird dir der Feind trotzdem den Nachschub abgraben.
Ok, dass ist eine recht gute Metapher. :)

Aber ich denke nicht, dass ich gegen Android-Designrichtlinien arbeite - ich wende sie einfach nur nicht an jeder Stelle an. Sicherlich werden Intents & Co. jenseits des Menüs, an welchem ich mich gerade versuche, eine sehr große Rolle spielen. Das streite ich keines Falls ab.

Und das XML-Layouting ist ja nur empfohlen, aber nicht zwingend: das steht sogar in der Doku von Google so drin. Insofern kann man auch nicht von "gegen Designrichtlinien" sprechen - maximal von "gegen Designempfehlung". ;)

I feel your pain - das hasse ich auch wie die Pest.
Danke, dass tut gut. :D

Und abschließend noch etwas: Ab Java 5 solltest du die Generics verwenden. Also ca. so:

Bzgl. Laufzeit egal, gibt dir aber Typsicherheit zur Compile-Zeit. Und punkto Typinferenz hättest du mit List<Object> ja auch nicht mehr Vorteile als mit List<T>.
Gut, dass du dies nochmal ansprichts. Ich wollte es ursprünglich sogar so umsetzen, bin aber dann am Adapter gescheitert:

Java:
public class ItemAdapter extends ArrayAdapter<Object> {

An dieser Stelle kann ich das <Object> leider nicht durch <T> erstzen.
Damit muss ich auch im Konstruktur <Object> verwenden:

Java:
	public ItemAdapter(Context context, List<Object> objects) {
		super(context, 0, objects);

Da ich somit an einer Stelle auf <Object> festgelegt war, habe ich mir gedacht, ich ziehe es dann auch bei ListViewEx so durch - damit erspare ich dem Nutzer der Klasse die Angabe der Generics.

Wobei ich es für ListViewEx natürlich unabhängig vom Adapter schon mit Generics umsetzen könnte, um den von dir erwähnten Vorteil zu erreichen. Ich denke, dass werde ich auch so machen...

[EDIT]Ich habe mich getäuscht: Eclipse meckert rum, wenn ich die Generics bei ListViewEx verwende.
Kann diese dennoch irgendwie verwenden?[/EDIT]
 
Zuletzt bearbeitet:

jf

Bekanntes Mitglied
[WR]Achtung: ich habe gerade bemerkt, dass ich Darstellungsfehler erhalte, wenn ich
Code:
if(convertView == null)
in der Methode getView() des ItemAdapters verwende: es werden oft mehrere Items hintereinander in eine Zeile geschrieben.[/WR]
Kann man dagegen etwas tun (außer evtl. XML-Layouting :D) - oder muss ich wirklich jedes Mal einen neues LinearLayout erstellen? :autsch:
 
Zuletzt bearbeitet:

jf

Bekanntes Mitglied
Hallo schlingel, dürfte ich nochmal anfragen, ob du evtl. doch noch eine Idee zu den Prolemen der blauen und roten Box hast?
 

schlingel

Gesperrter Benutzer
Ehrlich gesagt, kann ich mir das nicht erklären. Wie du weiter oben geschrieben hast, sollte das egal sein.

Poste mal einen Screenshot vom Darstellungsfehler (geht über DDMS) und den getView-Code noch einmal wie er jetzt ist.
 

jf

Bekanntes Mitglied
Punkt 1.)
Ehrlich gesagt, kann ich mir das nicht erklären. Wie du weiter oben geschrieben hast, sollte das egal sein.

Ich war wohl nur etwas erschrocken obgleichen der vielen angezeigten Fehler...
habe mir die Sache nochmal angeschaut: es muss überall noch ein Cast gemacht werden, dann läuft es! :)

Java:
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListAdapter;
import android.widget.ListView;

public class ListViewEx<T> extends ListView {
 
    // constructors //////////////////////////////////////////////////////////////
    
    public ListViewEx(Context context) {
        super(context);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, (List<Object>) new ArrayList<T>()) );
    }
    public ListViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, (List<Object>) new ArrayList<T>()) );
    }
    public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setAdapter( (ListAdapter)new ItemAdapter(context, new ArrayList<Object>()) );
    }
 
    
    // methods ///////////////////////////////////////////////////////////////////
 
    public void addItem(T item) {
        ((ItemAdapter)this.getAdapter()).add(item);
    }
 
    public void addItemAt(T item, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemAt(item, pos);
    }
 
    public void addItems(T[] items) {
        ((ItemAdapter)this.getAdapter()).addItems(items);
    }
 
    public void addItemsAt(Object[] items, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemsAt(items, pos);
    }
 
    public void addItems(List<T> list) {
        ((ItemAdapter)this.getAdapter()).addItems((List<Object>) list);
    }
 
    public void addItemsAt(List<T> list, int pos) {
        ((ItemAdapter)this.getAdapter()).addItemsAt((List<Object>) list, pos);
    }
 
    public void setItems(T[] array) {
        ((ItemAdapter)this.getAdapter()).setItems(array);
    }
    
    public void setData(List<T> list) {
        ((ItemAdapter)this.getAdapter()).setItems((List<Object>) list);
    }
 
}

Wobei Eclipse bei allen (List<Object>)list-Casts eine Warnung anzeigt, was nicht ganz so toll ist.
- Kann man evtl. noch den ItemAdapter irgendwie generisch umsetzen?
 

jf

Bekanntes Mitglied
Punkt 2.)
Poste mal einen Screenshot vom Darstellungsfehler (geht über DDMS) und den getView-Code noch einmal wie er jetzt ist.

Hier der getView()-Code:
Java:
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView == null) {
			convertView = new LinearLayout(m_context);
		}

		final TextView lblName = new TextView(m_context);
		final Object item = this.getItem(position);
		
		lblName.setText(item.toString());
		((LinearLayout)convertView).addView(lblName);

		return convertView;
	}

=> Der Darstellungsfehler tritt nicht auf, wenn ich die if(convertView == null)-Bedingung weg lasse.


PS:
Bildschirmfoto des Darstellungsfehlers ist im Anhang. (Was ist DDMS?)
 

Anhänge

  • Darstellungsfehler.png
    Darstellungsfehler.png
    86,9 KB · Aufrufe: 47
Zuletzt bearbeitet:

schlingel

Gesperrter Benutzer
Nope, das Problem mit den Generics bleibt bestehen. Ist so nicht vorgesehen was du vorhast, da du ja in dem Fall auch View mit Controller vermischt. Normalerweise hat man nur den Controller bzw. Adapter der die Item-Manipulation übernimmt.
Die ListView ist nur dazu da um den Adapter und die EventHandler umgehangen zu bekommen.

Punkto Anzeigefehler ist mir klar was nicht hinhaut: Du fügst ja die TextView dem Baum deiner LinearView hinzu. Das heißt jedes Mal wenn die View nicht null ist, wird eine weitere View angehängt anstatt den Text der alten neu zu setzen.

Wenn du es so machst, sollte es hinhauen: (Betonung auf sollte weil ich es nicht getestet habe :) Aber das Prinzip sollte klar sein.)

Java:
    private static final int ELEMENT_LABEL_ID = 1337;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
       final Object item = this.getItem(position); 
       TextView lblName = null;

        if(convertView == null) {
            convertView = new LinearLayout(m_context);
            lblName = new TextView(m_context);       
            lblName.setId(ELEMENT_LABEL_ID);     
            ((LinearLayout)convertView).addView(lblName); 
       } else {
           // in diesem Fall existiert ja das Label schon, also muss nur die Referenz hergestellt werden
           // um den text weiter unten setzen zu können.
           lblName = (TextView)convertView.findViewById(ELEMENT_LABEL_ID);
        }
 
        // element kann ruhig schon in der convertView drinnen sein bevor wir den Text setzen 
        lblName.setText(item.toString());
 
        return convertView;
    }
 

jf

Bekanntes Mitglied
Nope, das Problem mit den Generics bleibt bestehen. Ist so nicht vorgesehen was du vorhast, da du ja in dem Fall auch View mit Controller vermischt. Normalerweise hat man nur den Controller bzw. Adapter der die Item-Manipulation übernimmt.
Die ListView ist nur dazu da um den Adapter und die EventHandler umgehangen zu bekommen.
Ok.

Punkto Anzeigefehler ist mir klar was nicht hinhaut: Du fügst ja die TextView dem Baum deiner LinearView hinzu. Das heißt jedes Mal wenn die View nicht null ist, wird eine weitere View angehängt anstatt den Text der alten neu zu setzen.
Verstehe ich das richtig:
getView() ist also keine Methode um die Liste einmal zu initilaisieren (deshalb habe ich für jeden Eintrag eine neue TextView hinzugefügt), sondern wird jedes Mal aufgerufen, wenn ein Eintrag in den sichtbaren Bereich gezogen wird. - In diesem Fall wird ja erst aufs Display gezeichnet, wofür die Text-Information aus dem Controller benötigt wird?

Wenn du es so machst, sollte es hinhauen: (Betonung auf sollte weil ich es nicht getestet habe :) Aber das Prinzip sollte klar sein.)
Hat auf anhieb funktioniert - aber das Prinzip ist leider noch nicht ganz klar...
Ich habe Bauchschmerzen mit der
Code:
ELEMENT_LABEL_ID
: deren Verwendung müsste doch dazu führen, dass man nur eine ListViewEx-Instanz einsetzen kann (zumindest pro Activity), da dies eine statische Konstante ist.
Ich habe nun versucht, anstatt über
Code:
findViewById
einfach die TextView als Member der Klasse zu deklarieren. Die funktioniert auch in soweit, dass keine Exception bei der Ausführung geworfen wird. - Aber wenn man in der Liste nach unten rollt, werden plötzlich wieder die Einträge vom Anfang der Liste dargestellt.
Wenn ich an dieser Stelle nicht jedes Mal eine neue TextView anlegen muss, warum kann ich dann nicht mit der einen Referenzarbeiten, sondern muss den Umwege über eine ID gehen?
 

schlingel

Gesperrter Benutzer
Verstehe ich das richtig: [...]
Ja, ganz genau. Die bei Google haben sich für die ListView bzw. den Adapter was geniale einfaches überlegt. Es wird immer nur das gerendert was man auch sieht. Dadurch spart man CPU-Zeit und Speicher. Und durch das Wiederverwenden wird auch noch einmal CPU-Zeit gespart weil die Objekte nicht immer wieder initialisiert werden müssen.

Hat auf anhieb funktioniert - aber das Prinzip ist leider noch nicht ganz klar...
Ich habe Bauchschmerzen mit der ELEMENT_LABEL_ID : deren Verwendung müsste doch dazu führen, dass man nur eine ListViewEx-Instanz einsetzen kann (zumindest pro Activity), da dies eine statische Konstante ist.
Nein, eine ID muss immer nur innerhalb eines bestimmten View-Baums einzigartig sein. Da du ja lauter einzelne Views hast deren Kinder du ansprechen möchtest, macht es nichts, dass die ID fest definiert ist. Da du durch die verschiedenen convertView-Instanzen hier ganz leicht unterschieden werden kann.

Den Umweg über die ID musst du gehen, weil du ja bei jedem Aufruf von getView ein neues convertView-Element zurückliefern könntest und sicherstellen musst, dass du auch die richtige TextView verwendest. Mit einer einzelnen Referenz geht das nicht.

Du könntest natürlich auch eine Liste mit allen TextViews für jede Position führen und mit position ansprechen aber das würde alle oben genannten Vorteile zunichte machen. ListViews sollen ja auch mit 1000nden Einträgen fertig werden, das würde deine Liste dann nicht mehr leisten.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
T Bringen mir die Java-Basics irgendetwas für die Android-Programmierung Android & Cross-Platform Mobile Apps 4
B Android App Programmierung Einsteiger Problem Android & Cross-Platform Mobile Apps 4
C Android Programmierung speziell oder einfach Java Buch kaufen? Android & Cross-Platform Mobile Apps 3
R Android Programmierung Android & Cross-Platform Mobile Apps 3
M Hilfe bei App Programmierung für Android Android & Cross-Platform Mobile Apps 1
? Android einstieg in die Android Programmierung Android & Cross-Platform Mobile Apps 6
M App Programmierung: Thread starten und Variablenwerte oder Objekte mitgeben Android & Cross-Platform Mobile Apps 2
K Android Programmierung von App, wie lange muss ich lernen? Android & Cross-Platform Mobile Apps 10
D Android Spiele Programmierung Buch Android & Cross-Platform Mobile Apps 2
J Android-App-Programmierung - Ähnlich normaler Java-Programmierung? Android & Cross-Platform Mobile Apps 6
M Java und App Programmierung in 4 Monaten? Android & Cross-Platform Mobile Apps 7
W Android App Programmierung - Button ganz transparent machen Android & Cross-Platform Mobile Apps 3
A Java Symbian Programmierung Android & Cross-Platform Mobile Apps 4
K J2ME programmierung - startprobleme. Android & Cross-Platform Mobile Apps 7
R Java Programmierung auf Handys Android & Cross-Platform Mobile Apps 8
J Bücher über Java-Spiele-Programmierung Android & Cross-Platform Mobile Apps 3

Ähnliche Java Themen

Neue Themen


Oben