Ich habe natürlich danach gegoogelt - aber für Android sieht es eben noch etwas mau aus...Wie viel Zeit hast du denn investiert bevor du diese Frage gestellt hast? Gar keine?
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.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.
Ok, danke.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.
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;
}
}
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.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.
Straße aaa
Straße bbb -> Objekt xxx
Objekt yyy -> reparieren
austauschen
Objekt zzz
Straße ccc
Meine View sollte eigentlich das komplette Menü darstellen: mit dem Wechsel aller Untermenüs.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.
Ok.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, 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.Außerdem kannst du dem LinearLayout auch sagen, es soll nach unten und nicht nach rechts wachsen.
Würdest du dies nach obiger Eläuterung immer noch raten?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.
SchadeSo wie du es vorhast gibt's in Android nichts vorgefertigtes.
Hmm. Kann man die Views nicht dynamsich ändern und so die Animation für die Untermenüs erzeugen?Du müsstest für jedes "Untermenü" eine eigene Activity erzeugen.
Würde das auch mit Animationen gehen?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.
Das Anspringen eines jeden Menüs könnte man bei Bedarf auch bei einer einzelnen Komponente über eine spezielle Methode lösen.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.
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.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.
Kann man, soll man aber nicht. Operationen die am View-Baum durchgeführt werden sind wahnsinnig teuer.Kann man die Views nicht dynamsich ändern und so die Animation für die Untermenüs erzeugen?
Jein, denn die kommt vom System. Oder du machst das per ViewFlipper, siehe auch weiter unten.Würde das auch mit Animationen gehen?
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.Das Anspringen eines jeden Menüs könnte man bei Bedarf auch bei einer einzelnen Komponente über eine spezielle Methode lösen.
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.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.
Soll heißen, es wird keine Untermenü-Animation, sondern eher eine Untermenü-Ruckelaktion?Kann man, soll man aber nicht. Operationen die am View-Baum durchgeführt werden sind wahnsinnig teuer.
Ich versuche, was ich kann - leider ist die Zeit für eine Intensive Recherche viel zu knapp.Schau dir lieber mal den App-Lifecycle von Android an und lies dich in die Intent-Philosophie ein.
Ja, Swing ist das was ich kenne und auch recht gut finde.Du versuchst hier Swing-Pattern reinzubringen und da haben sie nichts verloren.
Die View würde ja dann das komplette Menü darstellen - damit hätten die Breadcrumbs auf anderen Screens eh 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.
Ok, das klingt gut. Ich werde es mir mal anschauen.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.
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.Soll heißen, es wird keine Untermenü-Animation, sondern eher eine Untermenü-Ruckelaktion?
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.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.
Nein, dass will man nicht. Dem stimme ich zu. - Ich finde aber auch JavaScript nicht so toll...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.
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.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.
Vielen Dank für das Beispiel!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.
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;
}
}
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;
}
}
TextEdit txtBxFeld1 = (TextEdit)convertView.findViewById(R.id.deineId);
Ok, aber durch das Überschreiben von getView bin ich doch auf View festgelegt.Das heißt die convertView muss eine ViewGroup sein, die deine Elemente enthält.
((ViewGroup) convertView).addView(lblName);
((ViewGroup) convertView).addView(lblPrice);
Ja, leider. - Aber überall wurde auch gesagt, dass man das UI auch in Java schreiben kann, auch wenn es nicht die bevorzugte Variante ist.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, ich habe auch schon damit angefangen - nur leider hat mich auch schon recht vieles dabei sehr enttäsucht...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.
Das ist korrekt. - Aber folgende Deklaration schreibt ja View als Typ vor: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.
public View getView(int position, View convertView, ViewGroup parent) {
Muss ich dieser ViewGroup dann auch die convertView (Typ View) hinzufügen?Du musst dir im Code eine ViewGroup erstellen, am besten ein LinearLayout oder sonst irgendetwas einfaches und dort dann die Elemente hinzufügen.
Ich glaube dir ja. Nur habe ich auch meine Philosophie und auch etwas Ehrgeiz, weshalb auch auch gerne mal gegen den Strom schwimme.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.
An dem ist doch auch Google schuld! :bae:Bevor du da Google den schwarzen Peter zuschieben magst, schau dir noch mal diesen Thread an. ;-)
Ok, aber ViewGroup ließ sich nicht instanzieren, habe daher das LinearLayout verwendet.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, 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.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.
Aber auch genügend, einen gut zu verstehenden zu bauen.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
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.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?
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.Aber auch genügend, einen gut zu verstehenden zu bauen. - Und das muss nicht zwangsweise Best Practices sein.
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.Prinzipiell hier ein Tipp von mir: Schau dir an wie der Dalvik GC arbeitet und wie man Apps optimiert.
Ja, dies ist die goldene Regel der Programmierung!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.
if(convertView == null)
Das hättest du mal unseren Vorgängern hier sagen sollen! :lol: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.
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!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.
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;
}
}
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);
}
}
ListViewEx list = new ListViewEx(context);
list.addItems(COUNTRIES);
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.Ich lasse mir gern vielen sagen, aber nicht, dass ich unstrukturiert und unsauber programmiere.
I feel your pain - das hasse ich auch wie die Pest.der komplette Code durchzogen mit alternierenden Präprozessor-Switches
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);
}
}
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.
Ok, dass ist eine recht gute Metapher.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.
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".
Danke, dass tut gut.I feel your pain - das hasse ich auch wie die Pest.
Gut, dass du dies nochmal ansprichts. Ich wollte es ursprünglich sogar so umsetzen, bin aber dann am Adapter gescheitert: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>.
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]
if(convertView == null)
Ehrlich gesagt, kann ich mir das nicht erklären. Wie du weiter oben geschrieben hast, sollte das egal sein.
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);
}
}
Poste mal einen Screenshot vom Darstellungsfehler (geht über DDMS) und den getView-Code noch einmal wie er jetzt ist.
@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;
}
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;
}
Ok.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.
Verstehe ich das richtig: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.
Hat auf anhieb funktioniert - aber das Prinzip ist leider noch nicht ganz klar...Wenn du es so machst, sollte es hinhauen: (Betonung auf sollte weil ich es nicht getestet habeAber das Prinzip sollte klar sein.)
ELEMENT_LABEL_ID
findViewById
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.Verstehe ich das richtig: [...]
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.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.