Designing a Javafx Mobile Application

Designing a Javafx Mobile Application

Designing a JavaFX Mobile application Fabrizio Giudici Tidalwave s.a.s. 214 AGENDA > Putting JavaFX (Mobile) in context > My case study > Main features of JavaFX (Mobile) > JavaFX Mobile through a few examples > Current status of JavaFX Mobile > Conclusion 2 About the speaker > [email protected] > Senior Architect, Mentor, Technical Writer > Working with Java (all the editions, plus exotic stuff) since 1996 > Working with Java ME since 1999 – First J2ME edition at JavaOne™ (Palm) – Developed a couple of research projects with the University of Genoa – Developed a couple of customer projects with my former company – Consulting for customer projects – Author of mobile FLOSS: windRose and blueBill Mobile > Co-leader of JUG Milano 3 What's wrong with JME > Everything was so good at the old, Palm times – Small devices → no big expectations – One reference platform – One pervasive runtime > With more devices, JME got fragmented – JSR as extension points (e.g. bluetooth, location) – Multiple combinations – Harder and harder to test – windRose pain 4 Hoping in Java FX Mobile > JavaFX announced by Sun Microsystems in 2007 – A specific scripting language + a runtime – Fight the “Ugly Java UI stereotype” – Re-designed UI controls – Integration with graphics designers workflow (e.g. NetBeans + Photoshop) – Profiles for multiple targets (desktop / web, mobile) > JavaFX Mobile profile – Can sit on top of JME (MSA: Mobile Service Architecture, a.k.a. JSR-248) – Maybe no more (or reduced) fragmentation? > Oracle commitment after the Sun buy > A fulfilled promise? – Answers at the end :-) 5 My case study: blueBill Mobile > “The field tool for birdwatchers” – Records geotagged observations – Reference guides with multimedia – Social capabilities (à la Google Friends) > Started in 2009 with JavaFX > Currently the most advanced version is the Android one – JavaFX version being redesigned (more later) > Demo 6 Main features of JavaFX > The languages is both imperative and declarative – Imperative: for normal processing – Declarative: mostly for the UI (no XML, no separate layout descriptor) Binding – Has got closures, mixins > Compiles to the same VM bytecode – Can mix with Java code (e.g. reference a Java library) – On the desktop, can be mixed with Swing (in addition to its own UI widgets) – On the mobile, runs its own UI widgets 7 Some code examples > Example 1: Calling JME code > Example 2: Posting contents (REST) > Example 3: Getting resources (REST) > Example 4: Binding (if time allows) 8 Example: calling JME code > Not much to say: it just works package it.tidalwave.bluebillmfx; import javax.microedition.location.Criteria; import javax.microedition.location.LocationProvider; import javafx.animation.Timeline; import javafx.animation.KeyFrame; import it.tidalwave.geo.mapfx.model.Coordinates; public class PositionTracker { public-read var currentPosition : Coordinates; def criteria = new Criteria(); var locationProvider : LocationProvider; postinit { locationProvider = LocationProvider.getInstance(criteria); pollingTimeline.play(); } 9 Example: calling JME code > Need to copy data in JavaFX classes if you want binding support function getPosition(): Void { def location = locationProvider.getLocation(5); // JME stuff def coordinates = location.getQualifiedCoordinates(); if (coordinates != null ) { def lat = coordinates.getLatitude(); def lon = coordinates.getLongitude(); currentPosition = Coordinates { latitude: lat; longitude: lon }; } } def pollingTimeline = Timeline { repeatCount: Timeline.INDEFINITE keyFrames: KeyFrame { time: 10s; action: periodicTask } }; function periodicTask(): Void { getPosition(); // update the map position, etc... } } 10 Example: posting contents > HttpRequest provides a skeleton public class ObservationRDFUpload extends HttpRequest { public-init var serviceURL = "http://myHost:8084/blueBillGeoService"; override public var method = HttpRequest.POST; public var content : String; override var output on replace { if (output != null) { output.write(content.getBytes()); output.close(); } } override function start(): Void { location = "{serviceURL}/postObservation"; // REST call setHeader("Content-Type", "application/rdf"); super.start(); } } 11 Example: posting contents > HttpRequest provides a skeleton def upload = ObservationRDFUpload { content: // ... assemble the string with the contents }; upload.start(); 12 Example: reading resources > The relevant model class public class Taxon { public-read protected var id : String; public-read var urlsLoaded = false; var urlsLoading = false; var imageURLs : String[]; // [] is a list (“sequence”), not an array var images : DeferredImage[]; public bound function getImage (index : Integer): DeferredImage { def dummy = loadImageURLs(); // assignment is a requirement for the bound function return images[index]; } public bound function imageCount(): Integer { return sizeof images; } 13 Example: reading resources > Getting a JSON resource catalog function loadImageURLs(): Boolean { if (not urlsLoading and not urlsLoaded) { urlsLoading = true; def fixedId = clean(id); // get rid of/escape “bad” chars such as :, #, etc... def url = "http://kenai.com/svn/bluebill-mobile~media/catalog/{fixedId}.json"; def request : HttpRequest = HttpRequest { // mere instantiation, not subclassing! location: url; method: HttpRequest.GET; onInput: function (is: InputStream) { // hey, this is not a switch() label! if (request.responseCode != 200) { imageURLs = "media/black_glossy_web20_icon_012.png"; } else { try { def parser = PullParser { documentType: PullParser.JSON; input: is; onEvent: parseEventCallback }; parser.parse(); } catch (e: Exception) { println("Exception {e} while parsing {url}"); } finally { is.close(); 14 } } } Example: reading resources > Getting a JSON resource catalog onException: function (e: Exception) { imageURLs = "media/black_glossy_web20_icon_012.png"; urlsLoaded = true; } onDone: function() { images = for (url in imageURLs) { DeferredImage { url: url }; } imageURLs = null; urlsLoaded = true; } } request.start(); } return urlsLoaded; } def parseEventCallback = function (event: Event): Void { if ((event.type == PullParser.TEXT) and (event.name == "url")) { insert event.text into imageURLs; } 15 } } Example: reading resources > Loading an image > JavaFX Image loads automatically when initialized var imageCounter = 0; public class DeferredImage { public-init var url : String; public-read var progress = bind image.progress; public-read var error = bind image.error; def id = imageCounter++; public-read var image : Image; public function load() { // start loading only when load() is called if (image == null) { image = Image { url: url backgroundLoading: true } } } 16 } Example: binding > Introducing TaxonSearchController – Manages a list of Taxons – Produces a filtered list of Taxons whose names match a substring – Provides a hint for autocompletion Eg. you type “He” That matches only “Heron ***” Hints is “Heron “ 17 Example: binding > Some relevant parts of Taxon public class Taxon { public-read protected var displayName : String; public-read protected var scientificName : String; public-read protected var id : String; override function toString() { return "{displayName} ({scientificName}) ({id})" } } 18 Example: binding > TaxonSearchController (1/3) public class TaxonSearchController { public var taxons: Taxon[]; public var filteredTaxons: Taxon[]; public var selectedTaxonIndex : Integer = -1; // set from the UI public var selectedTaxon = bind if (selectedTaxonIndex < 0) then null else filteredTaxons[selectedTaxonIndex]; // setting this property will trigger filtering public var filter = "" on replace { filteredTaxons = taxons[taxon | matches(taxon, filter)]; updateHint(); } public-read var hint = ""; // the auto-completed hint 19 Example: binding > TaxonSearchController (2/3) protected function matches (taxon : Taxon, string: String) : Boolean { if (string == "") { return true; } if (taxon.displayName.toLowerCase().startsWith(string.toLowerCase())) { return true; // more complex in the real case, as it also deals with scientific name // but not relevant now } return false; } 20 Example: binding > TaxonSearchController (3/3) protected function updateHint(): Void { def hintTry = commonLeadingSubstring(filteredTaxons); // // Sometimes it can't find a better auto-completion than the current filter, // hint = if (hintTry.length() > filter.length()) then hintTry else filter; } } 21 Example: binding > TaxonSearchScreen public class TaxonSearchScreen extends Container // e.g. a UI panel { // warning: there are some simplifications, but the concepts are here public-read def controller = TaxonSearchController { selectedTaxonIndex: bind list.selectedIndex; } def list = ListBox { // the list widget in the UI items: bind controller.filteredTaxons }; def hint = bind controller.hint on replace { if (hint != "") { filter = hint; } } var resetSelectionWhenFilterChanges = bind controller.selectedTaxon on replace { if (controller.selectedTaxon != null) { list.selectedIndex = 0; } } TextBox { // a text input box in the UI text: bind controller.filter with inverse } Text { // a label rendering a text in the UI content: bind "{sizeof controller.filteredTaxons} specie(s)" 22 } JavaFX mobile in 2009 > Development tools available (emulator, etc...) > First capable phones at J1: LG Incite, HTC Touch Diamond > Announced a “JavaFX Mobile player” – Would run JavaFX on MSA (JSR 248) JME phones > Looked very promising – That's why blueBill Mobile started with it 23 Status updates in 2010 >

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    28 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us