Donnerstag, 25. Februar 2010

"Rückgabewert" einer Klasse

Im Grunde genommen das gleiche Problem wie beim Wechseln zwischen Displayables:
Bevor ich aus einem als List implementierten Menü wieder zurück zum Hauptbildschirm wechsle, benötige ich noch eine Eingabe vom Anwender. Zum Beispiel einfach eine Zahl. Dazu muß natürlich ein neues Displayable angelegt und sichtbar gemacht werden, an dessen CommandListener dann die Kontrolle übergeht.

Falsch ist ein Ansatz folgender Art:


[1] dummy = new KlasseFuerNeuesDisplayable();
[2] // KlasseFuerNeuesDisplayable hat eine Funktion, die die vom Anwender

[3] // eingegebene Zahl zurückliefert
[4] retval   = KlasseFuerNeuesDisplayable.returnvalue();
[5] // ... und nun zurückschalten auf den Hauptbildschirm ...


Beim ersten Versuch wunderte ich mich, daß KlasseFuerNeuesDisplayable() gar nichts zu tun schien. Aber klar: Nach dem Konstruktor, der das neue Displayable anzeigt und den passenden CommandListener() setzt, geht's ja gleich im Text weiter - und zwar in Zeile [4]. Also den Returnvalue lesen, und sofort wieder auf den Hauptbildschirm umschalten. Das geht so schnell, daß davon nichts zu sehen ist - und ein Anwender auch gar keine Möglichkeit hat, irgendetwas zu tun.

Irgendwie müßte man nach Zeile [1] warten, bis KlasseFuerNeuesDisplay() ihren Job erledigt hat.

Eine Möglichkeit wäre wohl, mit Threads zu arbeiten: KlasseFuerNeuesDisplay() bekommt einen eigenen Thread spendiert. So richtig benötigen tut man den nicht - der CommandListener() läuft wohl ohnehin in einem anderen Thread-Kontext? - aber vielleicht könnte man die Ausführung der Zeilen 1-5 selbst in einem Thread-Objekt verpacken, den man nach Zeile [1] warten läßt, bis KlasseFuerNeuesDisplay() fertig ist. Etwas Vorsicht ist wohl angebracht, damit man nicht versehentlich den Thread schlafenlegt, der die Zeilen oben aufgerufen hat - im konkreten Fall steht der Code selbst in einem CommandListener(), und den sollte man wohl besser nicht anhalten (GUI-Hang? ... wäre mal auszuprobieren).

Warum aber überhaupt der Aufwand? - In meinem Fall ist das Öffnen der KlasseFuerNeuesDisplay() nicht immer notwendig, aber bevor auf den Hauptbildschirm zurückgeschaltet wird, muß noch eine etwas komplexere Aktion durchgeführt werden. Die steht aber in der naiven Implementierung an zwei Stellen: Einmal genau hier ab Zeile [5], und ein anderes mal wenn der Anwender seine Eingabe in KlasseFuerNeuesDisplay() getätigt hat. Denn dann soll es auch direkt zum Hauptbildschirm zurück, nicht mehr zum vorhergehenden Menü.

Meine "Lösung": Eine eigene Funktion, die alle notwendigen Aufräumarbeiten erledigt, und mit den entsprechenden Parametern einmal von KlasseFuerNeuesDisplay() aus ausgerufen wird, und einmal direkt vom Menü aus. Aber ob das der "richtige" Weg ist?

Freitag, 19. Februar 2010

Items in einer List() als "markiert" kennzeichnen?

Wie markiert man einen Eintrag in einer Liste als "markiert"? Und zwar nicht in Form eines "Radio-Button", wo nur ein Eintrag ausgewählt sein kann. Nein, ich möchte tatsächlich mehrere Einträge "markieren" können. Und auf eine einzelne "Markierung" auch reagieren.

List()
bietet eine Methode setSelectedIndex() an. Allerdings hat das bei einer EXPLICIT-Liste nicht die gewünschte Funktion. Nun könnte ich zwar eine MULTIPLE-Liste nehmen - das sieht im Emulator schon mal gar nicht so falsch aus - allerdings erhält die Anwendung dabei keine Mitteilung, wenn ein Eintrag an- oder abgewählt wurde. Die Mitteilung benötige ich aber ... also was tun?

Fehlt da was in JavaME?

Als Notbehelf verwende ich jetzt die EXPLICIT-Liste, und setzte einen "selected"-Font für ausgewählte Elemente. Den besorge ich mir über

_selectedFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC, Font.SIZE_MEDIUM);


Dazu benötige ich dann natürlich auch noch das Gegenteil:


_defaultFont = Font.getFont(0,0,0);


Nun gut. Das funktioniert im Prinzip. Aber "schön" ist anders. Vielleicht wird mir nichts anderes übrig bleiben, als zu jedem Eintrag ein "Image" hinzuzunehmen, das aus nichts anderem als einem Häkchen bzw. der Abwesenheit eines Häkchens besteht. Leider verträgt sich das nicht besonders gut damit, daß ich ohnehin noch ein Image zu den Liste-Einträgen hinzufügen wollte.

Dienstag, 5. Januar 2010

Wechseln zwischen Displayables

Ein Blick auf Beispielcode von Nokia zeigt eine mögliche Herangehensweise:

Jeder Screen (jedes Displayable) wird durch eine Klasse implementiert, die direkt vom entsprechenden Displayable, also z.B. List, abgeleitet wird. Der Konstruktor der Klasse erhält eine Referenz aufs MIDlet (also "this" der Klasse, die von MIDlet abgeleitet ist) sowie eine Referenz auf das Displayable, aus dem heraus das neue gestartet werden soll.

Für jemanden mit starkem (nicht mehr ganz taufrischem) C/C++-Background ist das zunächst einmal nicht gerade die offensichtlichste Herangehensweise. Für ein so kleines "Projekt" wie das meinige hier wäre eine globale Variable für die MIDlet-Referenz nicht falsch, da so jede Klasse eine eigene Kopie bekommt.

Da zudem der Control-Flow mit dem Aufruf des Konstruktors der Klasse für den neuen Screen auch an diese neue Klasse übergeht, wäre es aus C++-Sicht nicht offensichtlich, wie die Klasse je "destructed" werden kann. In Java muß sich da wohl schlicht und einfach der Garbage Collector drum kümmern? Referenzen gibt es nach dem Umschalten auf den vorherigen Screen nur noch innerhalb der Klasse selbst (hoffentlich ...). Wäre schön, sowas wie einen Destruktor in Java zu haben, allerdings gibt es in JavaME nicht mal finalize().

... die globale Variable könnte wohl über ein public static-Feld in der Haupt-MIDlet-Klasse nachgebaut werden ...

Mittwoch, 30. Dezember 2009

Ein High-Level-Design wäre nicht schlecht

Selbst bei einer so kleinen Applikation ist es nicht falsch, sich ein wenig Gedanken über den groben Aufbau zu machen.

Grundsätzlich soll es

  • einen "Hauptbildschirm" mit den zu erledigenden Einkäufen geben
  • von dort aus erreichbar einen Bildschirm mit definierbaren "Produkten", die zu den Einkäufen hinzugefügt werden können
  • auf Knopfdruck wird ein Einkauf "abgehakt"

Wie verpackt man das am besten in eine Klassenhierarchie? Und, nicht zu vergessen, wie implementiert man am vernünftigsten mehrere "Bildschirme" (Displayables) einer Applikation? - Mein erster Ansatz wird sein, eine Liste von "Items" zu definieren (die oben genannten "Produkte"), daraus ausgewählt eine Liste von Einkäufen. Eventuell beide direkt von List() ableiten. Die Displayables werden ein mal erzeugt, und dann bloß noch wie notwendig in den Vordergrund geschaltet (Display.getDisplay(...).setCurrent(...)).

Kleine Häßlichkeit am Rande: java.util.* ist in JavaME (jedenfalls MIDP 2.1 und CLDC 1.1) ziemlich (zu!) knapp bestückt. Es fehlt zum Beispiel eine verkettete Liste, die hier doch recht praktisch wäre. Zur Not tut's erst mal auch ein Vector().



Zum Nicht-wieder-vergessen
Falls die Application so gar nicht auf Tastendrücke reagieren will, obwohl Commands sichtbar sind, fehlt vielleicht einfach setCommandListener() für das entsprechende Displayable ...

Dienstag, 29. Dezember 2009

Nachtrag zum Dummy-Midlet

Wenn man das selbe MIDlet (selber Name) noch mal installieren möchte, gibt das E71 den Hinweis, daß das nicht möglich ist, da es noch laufe. Es bietet an, das laufende Programm zu beenden. Das funktioniert dann auch.

Außerdem: Wenn das MIDlet ein Displayable anzeigt (ein einfacher Alert() genügt, dann mit setCurrent() aktivieren) läßt es sich beenden, indem die "Auflegen"-Taste gedrückt wird, während das Display des MIDlet aktiv ist. Dann gibt's auch die Meldung, daß destroyApp() aufgerufen wurde. - Das funktioniert sowohl im Emulator als auch auf dem E71.


Zum Nicht-wieder-vergessen
Package für's Handy aus Eclipse/JavaME heraus erstellen:
Kontextmenü auf dem Projektnamen aufrufen -> J2ME -> Create Package

Sonntag, 27. Dezember 2009

SDK-Installation

Die Installation des S60 SDK verläuft theoretisch sehr einfach: Runterladen, Installer starten, einfach alles akzeptieren, fertig.
Leider funktioniert danach nicht alles so, wie es eigentlich sollte. Der "Installation Guide" (als .pdf im Installationsverzeichnis des SDK) beschreibt, wie man die Installation testen können sollte. Dazu gehört insbesondere auch, wie man ein einfaches HelloWorld - hier als C++-Programm - compilieren, packagen und mit dem Emulator testen können sollte. Es scheitert bei mir an

  • falscher Pfadangabe - nunja, viel gehört letztlich nicht dazu, zu bemerken, daß da im .pdf ein Stück vergessen wurde, aber muß das sein?
  • Build funktioniert nicht. Tut gar nichts. "No targets to make" oder so ähnlich.
An dem Schritt komme ich nicht vorbei.

Was soll's, ich will ja ohnehin kein C++ ausprobieren, und der Emulator funktioniert offenbar. Ein kurzer Test im JavaME-Eclipse-Environment schafft es jedenfalls, den Emulator mit einem einfachsten Beispielprogramm zu starten. Allerdings hängt das nun im "Debug Agent" auf dem emulierten Telefon - das mag aber auch an meinem Programm liegen.

...

Ok, merkwürdige Dinge passieren: Gegeben ein Dummy-MIDlet, das bloß aus Konstruktor, destroyApp(), pauseApp() und startApp() besteht, mit jeweils einem System.out.println("funcname"), um als poor-man's-Debugger sehen zu können, was genau passiert.
Im Emulator: Der Konstruktor wird aufgerufen, dann startApp(), dann kann ich machen was ich will, es geschieht nichts mehr. Das MIDlet taucht auch nicht in der Liste der geöffneten Programme auf.
Auf dem E71: Mangels Konsole ist zwar nicht zu sehen, was genau passiert, aber: Beim Starten wird ein leerer Bildschirm geöffnet, der als Titelzeile den MIDlet-Namen zeigt. Über "Hörer auflegen" oder ganz normalen Programmwechseln kommt man da wieder raus. Das MIDlet taucht auch hier nicht in der Liste der geöffneten Programme auf, ist aber als "geöffnet" in der Programmübersicht markiert.

Samstag, 12. Dezember 2009

Java lernen

Das e-learning aus dem vorherigen Posting lohnt sich tatsächlich - sehr zu empfehlen!

Ganz allgemein zu Java: Natürlich findet man im Web genug, um sich die Sprache stückweise selbst beizubringen - im Grunde genommen findet sich alles dazu im Web, schon seit Veröffentlichung der Sprache durch Sun - aber ein Buch ist mitunter doch recht praktisch. Insbesondere kann man sich damit auch mal eine Stunde in den Zug setzen. Die Wahrscheinlichkeit, die Sprache dabei richtig zu lernen statt nur nach Codebeispielen zu programmieren ist auch deutlich höher.

Nach ein wenig stöbern landete ich hier:
http://www.amazon.de/Java-Programming-Language-Addison-Wesley/dp/0321349806/
Bei Amazon.de zur Zeit auch sehr günstig zu haben ... günstiger als Amazon.com, trotz des aktuellen Dollar-Kurses?!?

Den Kommentaren nach ist "The Java Programming Language" nicht gerade für Programmier-Anfänger geeignet. Aber das bin ich ja nicht => bestellt.

Bis es da ist, reicht auch erst mal Trial&Error&Onlinedocs.