Codename One Developers Guide
Codename One Developers Guide
Table of Contents
................................................................................................................................. xv
About This Guide .................................................................................................... xvi
1. Introduction ............................................................................................................ 1
1.1. How Does Codename One Work? ............................................................. 1
1.1.1. Why Build Servers? .......................................................................... 2
1.1.2. Versions In Codename One ............................................................. 5
1.2. History ......................................................................................................... 6
1.3. Installation ................................................................................................... 7
1.3.1. Installing Codename One In NetBeans ............................................. 7
1.3.2. Installing Codename One In Eclipse ................................................. 9
1.3.3. Installing Codename One In IntelliJ IDEA ....................................... 11
1.4. Hello World Application ............................................................................. 12
1.4.1. The Source Code Of The Hello World App ..................................... 16
1.4.2. Building & Deploying On Devices ................................................... 27
1.5. Application Lifecycle .................................................................................. 30
1.5.1. Suspend/Resume ............................................................................ 31
2. Basics: Themes, Styles, Components & Layouts ................................................ 32
2.1. What Is A Theme, What Is A Style & What Is a Component? ................... 32
2.2. Native Theme ............................................................................................ 33
2.3. Component/Container Hierarchy ............................................................... 37
2.4. Layout Managers ...................................................................................... 38
2.4.1. Constraint Based Layout Managers ................................................ 39
2.4.2. Understanding Preferred Size ......................................................... 40
2.4.3. Flow Layout ..................................................................................... 42
2.4.4. Box Layout ...................................................................................... 47
2.4.5. Border Layout ................................................................................. 51
2.4.6. Grid Layout .....................................................................................
2.4.7. Table Layout ...................................................................................
2.4.8. Layered Layout ...............................................................................
2.4.9. GridBag Layout ...............................................................................
2.4.10. Group Layout ................................................................................
2.4.11. Mig Layout ....................................................................................
2.5. GUI Builder ...............................................................................................
2.5.1. Hello World .....................................................................................
3. Theme Basics .....................................................................................................
3.1. Understanding Codename One Themes ...................................................
iii
53
57
62
65
67
71
73
75
94
94
101
102
112
113
113
115
115
121
122
122
122
124
132
132
132
133
135
147
148
150
153
153
154
158
168
171
176
176
176
178
180
184
184
189
191
194
iv
199
201
203
204
208
211
213
214
214
216
218
219
221
225
226
226
226
227
228
231
231
237
240
246
247
249
259
263
266
271
275
282
283
289
291
297
302
306
307
310
317
318
319
324
324
324
324
331
333
334
338
341
343
344
344
345
346
347
348
350
351
352
353
354
355
359
360
363
364
365
365
366
vi
vii
369
374
374
374
377
377
380
383
383
383
385
387
390
390
391
391
392
392
393
395
398
400
401
402
403
404
406
409
416
416
417
418
418
420
420
424
431
431
431
432
435
435
436
437
437
437
438
439
440
440
442
442
443
446
447
447
450
451
456
458
459
463
464
465
481
481
483
493
494
498
500
501
viii
505
506
506
507
512
514
516
518
519
520
521
525
526
527
528
528
528
529
534
534
535
535
543
544
545
547
549
550
552
552
554
555
557
557
558
559
ix
579
580
581
582
584
638
641
655
658
661
665
666
667
668
668
669
670
673
674
674
674
674
674
675
675
675
676
677
679
682
684
687
688
689
695
700
701
702
702
703
703
xi
705
705
705
707
708
709
709
710
714
716
718
724
724
725
725
726
726
727
727
727
728
729
732
733
733
734
734
735
736
736
741
742
743
744
744
744
xii
766
766
769
769
770
771
771
772
784
784
785
xiii
List of Tables
2.1. Constraint properties ........................................................................................ 60
4.1. Theme Constants ........................................................................................... 136
4.2. Densities ......................................................................................................... 152
5.1. Java to JavaScript .......................................................................................... 304
5.2. JavaScript to Java .......................................................................................... 304
9.1. Transforms Device Support ............................................................................ 391
10.1. Event type map ............................................................................................ 439
11.1. Compare Storage, FileSystem & Jar Resources .......................................... 451
14.1. Build hints ..................................................................................................... 566
14.2. NativeInterface Supported Types ................................................................. 611
14.3. cn1lib structure ............................................................................................. 628
16.1. iOS Device Screenshot Resolutions ............................................................ 703
17.1. Property hints for the JavaScript port ........................................................... 741
xiv
xv
We also recommend that you check out the full JavaDoc reference for Codename
One which is very complete.
You can download the full Codename One source code from the Codename One git
2
repository where you can also edit the JavaDocs and submit pull requests to include
3
new features into Codename One .
1
2
3
https://www.codenameone.com/javadoc/
https://github.com/codenameone/CodenameOne/
https://www.codenameone.com/blog/how-to-use-the-codename-one-sources.html
xvi
Ismael Baum
Eric Coolman
Chen Fishbein
Steve Hannah
Matt
4
https://github.com/codenameone/
5
https://github.com/Isborg
6
https://twitter.com/ericcoolmandev
7
http://github.com/chen-fishbein/
8
http://github.com/shannah/
9
https://github.com/kheops37
xvii
xviii
Sidebar
Dig deeper into some details that dont quite fit into the current flow.
Here are common conventions for highlighting notes:
This is an important note
This convention is used when we refer to a button or a widget to press in the UI. E.g.
press the button labeled Press Here.
Quotes are presented as:
This is a quote
Bold is used for light emphasis on a specific word or two within a sentence.
Sidebar
We use sidebars for things that are an important detour, you can skip them while
reading but you might want to come back and read them later
xix
xx
Chapter1.Introduction
Codename One is a set of tools for mobile application development that derive a great
deal of its architecture from Java.
Codename Ones mission statement is:
Unify the complex and fragmented task of mobile device programming
into a single set of tools, APIs & services. As a result create a
more manageable approach to mobile application development without
sacrificing the power/control given to developers.
This effectively means bringing that old "Write Once Run Anywhere" (WORA)
Java mantra to mobile devices without "dumbing it down" to the lowest common
denominator.
Introduction
ParparVM and it will then compile the C source code using xcode & sign the resulting
binary using xcode. You can install the binary to your device or build a distribution binary
for the appstore. Since C code is generated it also means that your app will be "future
proof" in a case of changes from Apple. You can also inject Objective-C native code
into the app while keeping it 100% portable thanks to the "native interfaces" capability
of Codename One.
Subscribers can receive the C source code back using the include sources feature of
Codename One and use those sources for benchmarking, debugging on devices etc.
1
https://github.com/codenameone/CodenameOne/tree/master/vm
Introduction
The same is true for most other platforms. For the Android, J2ME & Blackberry the
standard Java code is executed as is.
2
Java 8 syntax is supported thru retrolambda installed on the Codename One servers.
This is used to convert bytecode seamlessly down to Java 5 syntax levels. Java 5
syntax is translated to the JDK 1.3 cldc subset on J2ME/Blackberry to provide those
language capabilities and APIs across all devices. This is done using a server based
bytecode processor based on retroweaver and a great deal of custom code. Notice that
this architecture is transparent to developers as the build servers abstract most of the
painful differences between devices.
Why ParparVM
3
On iOS, Codename One uses ParparVM which translates Java bytecode to C code
and boasts a non-blocking GC as well as 64 bit/bitcode support. This VM is fully
4
open source in the Codename One git repository . In the past Codename One used
5
XMLVM to generate native code in a very similar way but the XMLVM solution was
6
too generic for the needs of Codename One. ParparVM boasts a unique architecture
of translating code to C (similarly to XMLVM), because of that Codename One is the
only solution of its kind that can guarantee future iOS compatibility since the officially
supported iOS toolchain is always used instead of undocumented behaviors.
XMLVM could guarantee that in theory but it is no longer maintained.
https://github.com/orfjackal/retrolambda
3
https://github.com/codenameone/CodenameOne/tree/master/vm
4
https://github.com/codenameone/CodenameOne/
5
http://www.xmlvm.org/
6
https://github.com/codenameone/CodenameOne/tree/master/vm
Introduction
Simple & extensible - to work with ParparVM you need a basic understanding of C.
This is crucial for the fast moving world of mobile development, as Apple changes
things left and right we need a more agile VM.
Windows Phone/UWP
Codename One has 2 major Windows VM ports and 3 or 4 rendering pipelines within
those ports.
The old Windows Phone port used XMLVM to translate the Java bytecode to C#. Notice
that the XMLVM backend that translates to C# is very different from the one that was
used in the past to translates code for iOS.
Codename One now targets UWP by leveraging a modified version of iKVM to build
native Windows Universal Applications.
JavaScript Port
The JavaScript port of Codename One is based on the amazing work of the TeaVM
7
project . The team behind TeaVM effectively built a JVM that translates Java bytecode
into JavaScript source code while maintaining threading semantics using a very
imaginative approach.
The JavaScript port allows unmodified Codename One applications to run within a
desktop or mobile browser. The port itself is based on the HTML5 Canvas API to provide
a pixel perfect implementation of the Codename One APIs.
The JavaScript port is only available for Enterprise grade subscribers
of Codename One.
http://teavm.org:
https://github.com/orfjackal/retrolambda
Introduction
The desktop port creates a standard JavaSE application which is packaged with the
JRE and an installer.
The Desktop port is only available to pro grade subscribers of
Codename One.
Lightweight Components
What makes Codename One stand out is the approach it takes to UI where it
uses a "lightweight architecture" thus allowing the UI to work seamlessly across all
platforms. As a result most of the UI is developed in Java and is thus remarkably
portable and debuggable. The lightweight architecture still includes the ability to embed
"heavyweight" widgets into place among the "lightweights".
Introduction
to conform to this convention Codename One does make versioned releases which
contribute to the general confusion.
When a version of Codename One is released the version number refers to the libraries
at the time of the release. These libraries are then frozen and are made available to
9
developers who use the Versioned Builds feature. The plugin, which includes the
designer as well as all development that is unrelated to versioned builds continues with
its regular updates immediately after release. The same is true for the build servers
that move directly to their standard update cycle.
1.2.History
https://www.codenameone.com/how-do-i---get-repeatable-builds-build-against-a-consistent-version-of-
codename-one-use-the-versioning-feature.html
Introduction
1.3.Installation
1.3.1.Installing Codename One In NetBeans
For the purpose of this document we will focus mostly on the NetBeans IDE for
development, however most operations are almost identical in the Eclipse IDE as well.
Eclipse installation instructions are in the next section.
These instructions assume you have downloaded a recent version of NetBeans (at this
time 8.x), installed and launched it.
Select the ToolsPlugins menu option
Introduction
Introduction
Introduction
10
Introduction
Enter Codename One for the name and https://www.codenameone.com/
files/eclipse/site.xml for the location.
Select the entries & follow the wizard to install
11
Introduction
12
Introduction
Figure 1.11. New Project Dialog Step 2 - Select the project name/
location
Use a proper package name for the project!
13
Introduction
Introduction
Figure 1.14. The default theme created in the hello world wizard
15
Introduction
To run the application just press the IDEs play button and you should see something
similar to Figure 1.15, The default hello world Codename One app
import com.codename1.ui.Button;
import com.codename1.ui.Display;
import com.codename1.ui.FontImage;
import com.codename1.ui.Form;
import com.codename1.ui.Label;
16
Introduction
import com.codename1.ui.animations.CommonTransitions;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.FlowLayout;
import com.codename1.ui.layouts.LayeredLayout;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;
import com.codename1.ui.util.UITimer;
import java.io.IOException;
theme = UIManager.initFirstTheme("/theme");
// Pro only feature, uncomment if you have a pro subscription
// Log.bindCrashProtection(true);
if(current != null){
current.show();
return;
BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE));
icon.png"));
icon.png"));
FontImage.setMaterialIcon(getStarted, FontImage.MATERIAL_LINK);
getStarted.setUIID("GetStarted");
hi.addComponent(BorderLayout.CENTER,
LayeredLayout.encloseIn(
BoxLayout.encloseY(
new Label(theme.getImage("duke-no-
logos.png")),
);
getStarted
),
FlowLayout.encloseRightMiddle(apple)
17
Introduction
getStarted.addActionListener((e) -> {
Display.getInstance().execute("https://www.codenameone.com/
developers.html");
});
if(apple.getParent() != null) {
apple.getParent().replace(apple, android,
CommonTransitions.createFade(500));
} else {
if(android.getParent() != null) {
android.getParent().replace(android, windows,
CommonTransitions.createFade(500));
} else {
windows.getParent().replace(windows, apple,
CommonTransitions.createFade(500));
}
}
}).schedule(2200, true, hi);
hi.show();
current = Display.getInstance().getCurrent();
The class package and import statements should be self explanatory so well focus on
the class itself:
public class SuperDuperApp {
private Form current;
18
Introduction
}
public void destroy() {
}
The main class doesnt implement any interface or extend a base class, however the
Codename One runtime expects it to have these 4 methods and maintain the method
signatures.
A common mistake developers make is writing code that throws a
checked exception and then letting the IDE "auto-correct" the code
by adding a throws clause to the parent method. This will fail during
a device build.
We also keep two variables by default in a hello world application:
theme - allows us to set the look of the application. A theme object maps to the
theme.res file where a lot of data can be stored e.g. images, localization etc.
10
current - the current application Form . A Form is the top level class
responsible for placing a UI in Codename One. Think of it as you would think
about a Frame in AWT, JFrame in Swing, Activity + View in Android,
UIController + View in iOS & the html tag in HTML.
This variable is used for the Suspend/Resume Behavior of the application.
The 4 builtin methods of the Codename One main class include:
init(Object) - invoked when the application is started but not when its restored
from the background. E.g. if the app isnt running, init will be invoked. However,
if the app was minimized and is then restored init will not be invoked.
This is a good place to put generic initializations of variables and the likes. We
specifically use it to initialize the theme which is something we normally need to do
only once per application execution.
The object passed to init is the native OS context object, it can be null but can be
ignored for 99% of the use cases.
start() - the start method is invoked with every launch of the app. This includes
restoring a minimized application. This is very useful for initializing UIs which usually
need to be refreshed both when the user launches the app and when he returns to
it after leaving it in the background for any duration.
10
http://codenameone.com/javadoc/com/codename1/ui/Form.html
19
Introduction
stop() - stop is invoked when the user minimizes the app. If you have ongoing
operations (e.g. download/media) you should stop them here or the operating
system might kill your application due to CPU usage in the background.
destroy() - this callback isnt guaranteed since an OS might kill the app and
neglect to invoke this method. However, most OSs will invoke this method before
completely closing the app. Since stop() will be invoked first its far better to write
code there.
By default Codename One applications save/restore the current form with code like this:
public void start() {
if(current != null){
current.show();
return;
current = Display.getInstance().getCurrent();
Now all that remains is the content of the start() method, well break the body into
a few pieces for clarity:
Form hi = new Form("Welcome", new
BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE));
11
In this block we create a new Form named hi . We give it the title "Welcome"
which will be displayed at the top. We also define the layout manager of the form to
12
BorderLayout which allows us to place a component in one of 5 positions. In this case
the BorderLayout allows us to place the component in the center of the screen.
Normally a BorderLayout stretches the center component to take up the whole
screen but we want Duke to be completely centered so we ask the layout to use the
CENTER_BEHAVIOR_CENTER_ABSOLUTE behavior.
You can learn more about positioning components with layouts in the Layout Managers
section.
11
12
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
20
Introduction
We create the images for the logos that fade in Dukes hand. We use the Label
class to display the images. Notice that we get the images themselves from the theme
resource file.
Multi Images
We could load a PNG directly from file but using the resource file carries an
advantage as we can use multi images. Multi-images are a set of images adapted
14
to device density .
A high resolution image is used during development and the Codename One
designer adapts it to the various DPIs. This simplifies the process of dealing with
multiple device densities.
You can learn more about multi-images in the Section 4.7, Understanding
Images & Multi-Images section.
FontImage.setMaterialIcon(getStarted, FontImage.MATERIAL_LINK);
15
The getStarted Button is pretty self explanatory however the next line installing
the font image isnt as clear.
16
FontImage allows us to take a font (TTF) and use it as an icon or an image. This is
quite powerful as fonts are rendered smoothly in every device density & can easily be
adapted to theme conventions/colors.
17
Codename One has the material design icon font built into it, this provides a good
set of basic icons that all applications can reuse across all platforms. You can also pick
13
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
14
density indicates the amount of pixels for a given square inch of screen space e.g. an older iOS device
would have 160ppi (pixels per inch) whereas iPhone 6+ has 540 ppi!
15
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
16
https://www.codenameone.com/javadoc/com/codename1/ui/FontImage.html
17
https://www.google.com/design/icons/
21
Introduction
a custom icon font and make use of that using one of the many resources available
on the web.
The FontImage class allows us to create an Image
19
18
to use setMaterialIcon . The benefit of using that approach is simple, this method
automatically uses the buttons styling (color, font size etc.) for the icon image.
getStarted.setUIID("GetStarted");
20
The setUIID
method allows us to define the theme styling used for a given
component. By default a button would use the "Button" UIID so by changing this
the button will have a different look.
To understand what that would look like just double click the theme.res file and
select the theme. Then double click the "GetStarted" entry in the list, you should see
something like this:
18
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
19
https://www.codenameone.com/javadoc/com/codename1/ui/FontImage.html#setMaterialIconcom.codename1.ui.Label-char20
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html#setUIID-java.lang.String-
22
Introduction
new Label(theme.getImage("duke-no-logos.png")),
);
getStarted
),
FlowLayout.encloseRightMiddle(apple)
The code above is a bit terse, it uses the shortened syntax for Codename One, if you
are used to other frameworks a slightly more verbose version of the same code might
be clearer:
Container tmpBoxLayout = new Container(new BoxLayout(BoxLayout.Y_AXIS));
tmpBoxLayout.add(new Label(theme.getImage("duke-no-logos.png")));
23
Introduction
tmpBoxLayout.add(getStarted);
21
Container
22
The Component -> Container Hierarchy section discusses this subject further.
23
Then we place two containers, the first is a BoxLayout container on the Y axis. This
means the elements within this layout will order themselves vertically in a column. We
place the image for Duke within this layout followed by the button.
26
21
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
22
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
23
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
24
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/LayeredLayout.html
25
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BoxLayout.html
26
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.html
24
Introduction
Figure 1.17. Component hierarchy for the getting started hello world
app
getStarted.addActionListener((e) -> {
Display.getInstance().execute("https://www.codenameone.com/
developers.html");
});
We can use event callbacks to perform actions within a Codename One application. In
this case we are launching the browser to the Codename One website.
This code snippet uses Java 8 lambda syntax for simplicity. It is
functionally equivalent to this listing.
getStarted.addActionListener(new ActionListenr() {
public void actionPerformed(ActionEvent e) {
Display.getInstance().execute("https://www.codenameone.com/
developers.html");
}
});
You can learn more about event handling in Codename One in the Events Section.
new UITimer(() -> {
//...
25
Introduction
hi.show();
27
The UITimer class allows us to receive a callback within a fixed period of time. Here
we schedule the timer to repeat (the true argument) every 2200 milliseconds. We
need to bind this timer to the parent form, when we navigate to a different form it will
no longer be active.
Java contains a Timer class within the java.util package.
However its invoke within a separate thread which can cause issues
when dealing with UI. The UITimer class can be used to handle
UI in a seamless way.
As the final line of code in this demo we just show the form which should be self
explanatory.
if(apple.getParent() != null) {
apple.getParent().replace(apple, android,
CommonTransitions.createFade(500));
} else {
if(android.getParent() != null) {
android.getParent().replace(android, windows,
CommonTransitions.createFade(500));
} else {
windows.getParent().replace(windows, apple,
CommonTransitions.createFade(500));
}
The UITimer callback just replaces one component with another and uses the fade
28
29
transition for the replace operation.
It checks the current state by checking if the parent component is null . E.g. when
we replace the apple label with the android label the parent container for apple
will become null .
27
28
https://www.codenameone.com/javadoc/com/codename1/ui/util/UITimer.html
https://www.codenameone.com/javadoc/com/codename1/ui/animations/
CommonTransitions.html#createFade-int29
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html#replacecom.codename1.ui.Component-com.codename1.ui.Component-com.codename1.ui.animations.Transition-
26
Introduction
Certificates
The most crucial thing for a final app is the developer certificate. The certificate indicates
who the author of the app is and without it you wont be able to ship your app or update
your app.
Backup your Android certificate and save its password! We cant
stress this enough!
If you lose your Android certificate you will not be able to update your
app.
To create an Android or iOS certificate just use the generate button within the respective
section in the preferences.
27
Introduction
To generate an iOS certificate you will need an Apple developer
account which needs to be purchased from Apple and has a waiting
period for approval (less than a week).
Certificates and provisioning are a big & complex subject that we cover in depth in the
signing section.
You can reuse both Android and iOS certificates for all your
applications. However, for iOS you will need to create provisioning
profiles for every application.
Sending A Build
In order to get a native application for the devices you need to send a build to the build
server. For that purpose you will need to signup at https://www.codenameone.com/ and
click on the email validation link.
Once you signed up you can right click the project and select one of the build target
options and a build will be sent to the servers.
Desktop & JavaScript builds are for high grade paying subscriptions.
Free subscribers have a build quota limit imposed monthly and a jar
size limit. This are in place to prevent excessive build server resource
consumption.
28
Introduction
Figure 1.19. Right click menu options for sending device builds
Once the build is sent successfully you can get the result at https://
www.codenameone.com/build-server.html
29
Introduction
You can install the application either by scanning the QR code or emailing the install
link to your account (using the e-mail Link button).
You can download the binaries in order to upload them to the appstores.
1.5.Application Lifecycle
Now that we have a basic understanding of Codename One its probably a good point
to explain a core idea about the code above. Every Codename One application has a
main class which serves as its "entry point". This class has the following 4 methods:
public void init(Object context) {}
public void start() {}
public void stop() {}
These provide a set of simplified hooks into the application state in the operating
system.
30
Introduction
Android developers are used to Activity which provides a similar
set of methods and far more functionality. However, since Activities
are unique to Android their functionality cannot be reproduced in a
portable way.
Applications are always launched with a call to init(Object) , for historical reasons
a native context object is passed but its often null and when it isnt we dont really need
it anyway.
All Codename One callback methods are invoked on the event
dispatch thread (EDT) which is the main thread of Codename One.
Doing things like sleep loops or long running tasks within them is
strictly prohibited.
init(Object) is a great place to initialize everything that needs to always be there.
E.g. your application theme, crash protection etc.
start() is invoked immediately after init(Object) and every time an app is
restored from background (more on that below).
stop() is invoked when the app is sent to background or minimized.
destroy() might be invoked when an app is killed by the OS. There is no guarantee
that destroy will be invoked!
1.5.1.Suspend/Resume
Suspend happens when an app is minimized e.g. when a user presses the home button,
when an incoming call takes over the phone etc.
These operations suspend the app and send it into suspended state. If the user chooses
to return to a running app the app isnt started from scratch, its resumed.
The behavior of the app during its suspended state is very OS specific but all OSs place
heavy restrictions on background operations. When an app is minimized its expected
that it will quit all non-essential work (e.g. networking operations).
This means you should stop all of the "expensive" operations such as networking,
GPS etc. when the stop() method is invoked and potentially restore them when
start() is invoked.
You can simulate suspend/resume in the simulator menu, this wont simulate the OS
killing of your app but will allow you to debug the logic in the start() / stop()
methods.
31
Every visual element within Codename One is a Component , the button on the screen
2
is a Component and so is the Form in which its placed. This is all represented within
the Component , which is probably the most central class in Codename One.
Every component has 4 standard style objects associated with it: Selected ,
Unselected , Disabled & Pressed .
If you wish to set a value to all component states you can use
getAllStyles() which implicitly sets all the different styles.
Notice that you must use it for write operations ( set ) and never use
it for read operations ( get ).
Only one style is applicable at any given time and it can be queried via the
getStyle() method. A style contains the colors, fonts, border and spacing
information relating to how the component is presented to the user.
You should not use getStyle() in normal execution but instead
use
getUnselectedStyle() ,
getSelectedStyle() ,
getPressedStyle()
&
getDisabledStyle() .
The
reasoning is simple, getStyle() might return any one of those 4
depending on component state and you might not get the result you
want. You should only use`getStyle()` when painting the component
(e.g. in the paint callback method) to query information and not
to set information.
1
2
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
32
2.2.Native Theme
By default Codename One applications are created with a theme that derives from
the builtin OS native theme. You can add additional themes into the resource file by
pressing the Add A New Theme button. You can also create multiple themes and ship
them with your app or download them dynamically.
You can create a theme from scratch or customize one of the Codename one themes
to any look you desire.
To preview the look of your theme in the various platforms use the
Native Theme menu option in the designer.
Figure 2.2. The Color tab for the get started button theme settings
The alignment of the text is pretty simple, notice that the alignment style attribute applies
to text and doesnt apply to other elements. To align other elements we use layout
manager logic.
34
Figure 2.3. The alignment tab for the get started button theme
settings
Padding can be expressed in pixels, millimeters (approximate) or percentages of the
screen size.
We recommend using millimeters for all measurements to keep the
code portable for various device DPIs.
35
Figure 2.4. The padding tab for the get started button theme settings
The font uses native OS light font but has a fallback for older OSs that dont support
truetype fonts. The "True Type" font will be applicable for most modern OSs. In the
case of the "native:" fonts Android devices will use Roboto whereas iOS devices will
use Helvetica Neue. You can supply your own TTF and work with that.
Since Codename One cannot legally ship Helvetica Neue fonts the
simulator will fallback to Roboto on PCs.
At the time of this writing the desktop/simulator version of the Roboto
font doesnt support many common character sets (languages). This
will have no effect on an Android device where thenative font works
properly.
36
Figure 2.5. The font tab for the get started button theme settings
2.3.Component/Container Hierarchy
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
37
2.4.Layout Managers
5
Layout managers are installed on the Container class and effectively position the
Components within the given container. The Layout class is abstract and allows
users to create their own layout managers, however Codename One ships with multiple
layout managers allowing for the creation of many layout types.
The layout managers are designed to allow developers to build user interfaces that
seamlessly adapt to all resolutions and thus dont state the component size/position
but rather the intended layout behavior.
To install a layout manager one does something like this:
Container c = new Container(new BoxLayout(BoxLayout.X_AXIS));
c.addComponent(new Button("1"));
c.addComponent(new Button("2"));
c.addComponent(new Button("3"));
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/Layout.html
38
The add method can also accept an Image or a String. This will
effectively wrap such data in a label so add("abc") would be
equivalent to add(new Label("abc")) .
There is an even shorter version since most layout managers have helper methods to
6
7
facilitate smaller code sizes e.g. BoxLayout has encloseX(Component) that can
be used as such:
Container c = BoxLayout.encloseX(new Button("1"),
15
).
6
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BoxLayout.html
7
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BoxLayout.html#encloseXcom.codename1.ui.Component8
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
9
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
10
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/TableLayout.html
11
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GridBagLayout.html
12
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GroupLayout.html
13
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/mig/MigLayout.html
14
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
15
https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.html
39
16
This will stretch button 1 across the center of the container and buttons 2-3 will be
stretched horizontally at the top and bottom of the container.
The order to adding doesnt mean much for most constraint based
layouts involved.
Like the above sample code this too can be written using terse syntax e.g.:
Container c = new Container(new BorderLayout()).
This can also be written with a helper method from the layout e.g.
Container c = BorderLayout.center(new Button("1")).
add(BorderLayout.NORTH, new Button("2")).
add(BorderLayout.SOUTH, new Button("3"));
The Component class contains many useful methods, one of the most important ones
is calcPreferredSize() which is invoked to recalculate the size a component
wants when something changes
By default Codename One invokes the getPreferredSize()
method and not calcPreferredSize() directly.
getPreferredSize() invokes calcPreferredSize() &
caches the value.
16
17
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
40
18
BorderLayout resizes the center component by default (and the other components
on one of their axis).
You can define a group of components to have the same preferred width or height by
using the setSameWidth & setSameHeight methods:
Component.setSameWidth(cmp1, cmp2, cmp3, cmp4);
Component.setSameHeight(cmp5, cmp6, cmp7);
18
19
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
41
2.4.3.Flow Layout
Flow layout can be used to just let the components flow horizontally and break a line
when reaching the edge of the container. It is the default layout manager for containers,
but because it is so flexible it is often problematic as it can cause the preferred size
21
of the Container to provide false/misleading information, triggering endless layout
reflows.
20
21
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
42
add(new Label("Second")).
add(new Label("Third")).
add(new Label("Fourth")).
add(new Label("Fifth"));
hi.show();
new Label("Second"),
new Label("Third"),
new Label("Fourth"),
new Label("Fifth")));
Flow layout can be aligned to the left (the default), to the center, or to the right. It can
also be vertically aligned to the top (the default), middle (center), or bottom.
43
44
45
Figure 2.11. Flow layout aligned to the center horizontally & the
middle vertically
Components within the flow layout get their natural preferred size by default and are
not stretched in any axis.
The natural sizing behavior is often used to prevent other
layout managers from stretching components. E.g. if we have a
border layout element in the south and we want it to keep its
natural size instead of adding the element to the south directly
we can wrap it using parent.add(BorderLayout.SOUTH,
FlowLayout.encloseCenter(dontGrowThisComponent)) .
46
2.4.4.Box Layout
box-layout-x-no-grow
22
add(new Label("Second")).
add(new Label("Third")).
add(new Label("Fourth")).
add(new Label("Fifth"));
22
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BoxLayout.html
47
new Label("Fourth"),
new Label("Fifth")));
48
50
2.4.5.Border Layout
Border layout is quite unique, its a constraint-based layout that can place up to 5
components in one of the 5 positions: NORTH , SOUTH , EAST , WEST or CENTER .
Form hi = new Form("Border Layout", new BorderLayout());
hi.add(BorderLayout.CENTER, new Label("Center")).
add(BorderLayout.SOUTH, new Label("South")).
add(BorderLayout.NORTH, new Label("North")).
add(BorderLayout.EAST, new Label("East")).
add(BorderLayout.WEST, new Label("West"));
hi.show();
The layout always stretches the NORTH / SOUTH components on the X-axis to
completely fill the container and the EAST / WEST components on the Y-axis. The
center component is stretched to fill the remaining area by default. However, the
setCenterBehavior allows us to manipulate the behavior of the center component
so it is placed in the center without stretching.
23
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
51
((BorderLayout)hi.getLayout()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER);
hi.add(BorderLayout.CENTER, new Label("Center")).
add(BorderLayout.SOUTH, new Label("South")).
add(BorderLayout.NORTH, new Label("North")).
add(BorderLayout.EAST, new Label("East")).
add(BorderLayout.WEST, new Label("West"));
hi.show();
Results in:
24
RTL = Right To Left or Bidi = bi-directional. A common term used for languages such as Hebrew or
Arabic that are written from the right to left direction hence all the UI needs to be "reversed". Bidi denotes
the fact that while the language is written from right to left, the numbers are still written in the other
direction hence two directions
52
24
2.4.6.Grid Layout
25
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GridLayout.html
53
add(new Label("Second")).
add(new Label("Third")).
add(new Label("Fourth")).
add(new Label("Fifth"));
55
new Label("Fourth"),
new Label("Fifth")));
56
2.4.7.Table Layout
26
The TableLayout is a very elaborate constraint based layout manager that can
arrange elements in rows/columns while defining constraints to control complex
behavior such as spanning, alignment/weight etc.
The table layout is in the com.codename1.ui.table package
and not in the layouts package.
This is due to the fact that TableLayout was originally designed
27
for the Table class.
Despite being constraint based the table layout isnt strict about constraints and will
implicitly add a constraint when one is missing.
Unlike grid layout table layout wont implicitly add a row if the row/
column count is incorrect
https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.html
https://www.codenameone.com/javadoc/com/codename1/ui/table/Table.html
57
add(new Label("Second")).
add(new Label("Third")).
add(new Label("Fourth")).
add(new Label("Fifth"));
hi.show();
Figure 2.22. 2x2 TableLayout with 5 elements, notice that the last
element is missing
Table layout supports the ability to grow the last column which can be enabled using
the setGrowHorizontally method. You can also use a shortened terse syntax to
58
new Label("Fourth"),
new Label("Fifth")));
59
Table layout works with a Constraint instance that can communicate our intentions
into the layout manager. Such constraints can include more than one attribute e.g. span
and height.
Table layout constraints cant be reused for more than one
component.
The constraint class supports the following attributes
row
width
height
spanHorizontal
spanVertical
horizontalAlign
verticalAlign
28
https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.Constraint.html
60
The table layout constraint sample tries to demonstrate some of the unique things you
can do with constraints.
Due to the complexity of the code below we annotate lines with
numbers and follow up on individual lines.
new Label("AAA")).
add(tl.createConstraint().
horizontalSpan(2).
heightPercentage(80).
verticalAlign(Component.CENTER).
horizontalAlign(Component.CENTER),
new Label("Span H")).
add(new Label("BBB")).
add(tl.createConstraint().
widthPercentage(60).
heightPercentage(20),
new Label("CCC")).
add(tl.createConstraint().
widthPercentage(20),
new Label("DDD"));
2.4.8.Layered Layout
29
The LayeredLayout places the components in order one on top of the other and
sizes them all to the size of the largest component. This is useful when trying to create
an overlay on top of an existing component. E.g. an x button to allow removing the
component.
29
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/LayeredLayout.html
62
Figure 2.25. The X on this button was placed there using the layered
layout code below
The code to generate this UI is slightly complex and contains very little relevant pieces.
The only truly relevant piece is this block:
hi.add(LayeredLayout.encloseIn(settingsLabel,
FlowLayout.encloseRight(close)));
3. We use FlowLayout
32
The layered layout sizes all components to the exact same size one
on top of the other. It usually requires that we use another container
within; in order to position the components correctly.
This is the full source of the example for completeness:
Form hi = new Form("Layered Layout");
int w = Math.min(Display.getInstance().getDisplayWidth(),
Display.getInstance().getDisplayHeight());
close.setUIID("Container");
close.getAllStyles().setFgColor(0xff0000);
FontImage.setMaterialIcon(close, FontImage.MATERIAL_CLOSE);
hi.add(LayeredLayout.encloseIn(settingsLabel,
FlowLayout.encloseRight(close)));
Forms have a built in layered layout that you can access via getLayeredPane() ,
this allows you to overlay elements on top of the content pane.
The layered pane is used internally by components such as InteractionDialog
34
AutoComplete etc.
30
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/LayeredLayout.html
31
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
32
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/FlowLayout.html
33
https://www.codenameone.com/javadoc/com/codename1/components/InteractionDialog.html
34
https://www.codenameone.com/javadoc/com/codename1/ui/AutoCompleteTextField.html
64
33
2.4.9.GridBag Layout
36
hi.setLayout(new GridBagLayout());
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
hi.addComponent(c, button);
button = new Button("Button 2");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
35
36
37
https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GridBagLayout.html
http://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html
65
37
to
c.gridy = 0;
hi.addComponent(c, button);
button = new Button("Button 3");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 2;
c.gridy = 0;
hi.addComponent(c, button);
button = new Button("Long-Named Button 4");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 40;
c.weightx = 0.0;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
hi.addComponent(c, button);
button = new Button("5");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 0;
c.weighty = 1.0;
//reset to default
//top padding
c.gridx = 1;
c.gridy = 2;
//third row
c.gridwidth = 2;
hi.addComponent(c, button);
Notice that because of the way gridbag works we didnt provide any terse syntax API
for it although it should be possible.
66
2.4.10.Group Layout
38
GroupLayout is a layout that would be familiar to the users of the NetBeans GUI
39
builder (Matisse) . Its a layout manager thats really hard to use for manual coding
but is remarkably powerful for some really elaborate use cases.
It was originally added during the LWUIT days as part of an internal attempt to port
Matisse to LWUIT. It is still useful to this day as developers copy and paste Matisse
code into Codename One and produce very elaborate layouts with drag & drop.
Since the layout is based on an older version of group layout some things need to be
adapted in the code or you should use the special "compatibility" library for Matisse to
get better interaction. We also recommend tweeking Matisse to use import statements
instead of full package names, that way if you use Label just changing the awt import
40
to a Codename One import will make it use Label .
38
39
40
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GroupLayout.html
https://netbeans.org/features/java/swing.html
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
67
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.add(label1, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(label4, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label3, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
68
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
.add(label5, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(layout.createSequentialGroup()
.add(label6, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(label7, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)))
.addContainerGap(296, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(GroupLayout.TRAILING)
.add(label2, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label1, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addPreferredGap(LayoutStyle.RELATED)
.add(label3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(label4, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(label5, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.RELATED)
.add(layout.createParallelGroup(GroupLayout.LEADING)
.add(label6, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.add(label7, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
.addContainerGap(150, Short.MAX_VALUE))
);
69
70
2.4.11.Mig Layout
MigLayout
41
42
. As a
cells.
add(new Label("Forth")).
occupied cells.
add(new Label("Sixth")).
add(new Label("Seventh"));
hi.show();
41
42
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/mig/MigLayout.html
http://www.miglayout.com/QuickStart.pdf
71
2.5.GUI Builder
The GUI builder allows us to arrange components visually within a UI using drag & drop,
property sheets etc. With the GUI builder we can create elaborate, rich UIs without
writing the layout code.
The new GUI builder is still in beta state at this time, it shouldnt be
confused with the old GUI builder that is a part of the Codename
One designer tool
The new GUI builder is a standalone application that you launch from the right
click menu by selecting a form as explained below. Here are screenshots of both
to help you differentiate:
73
74
2.5.1.Hello World
Creating a hello world app in the new GUI builder is actually pretty trivial, you need to
start with a regular handcoded application. Not a GUI builder application as it refers
to the old GUI builder!
Creating a new hello world is similar for all IDEs and is covered in all the getting started
tutorials for the various IDEs specifically in NetBeans
43
, IntelliJ
44
& Eclipse
45
The new GUI builder requires Java 8. This means the IDE itself
needs to run on top of Java 8!
Following are the instructions for creating a form and launching the GUI builder. While
they are similar there are minor IDE differences. Usage of the GUI builder is identical
in all IDEs as the GUI builder is a separate application.
NetBeans
In NetBeans you need to follow these 4 steps:
http://www.codenameone.com/how-do-i---create-a-basic-hello-world-applicationsend-it-to-my-deviceusing-netbeans.html
44
http://www.codenameone.com/how-do-i---create-a-basic-hello-world-applicationsend-it-to-my-deviceusing-intellij-idea.html
45
http://www.codenameone.com/how-do-i---create-a-basic-hello-world-applicationsend-it-to-my-deviceusing-eclipse.html
75
Figure 2.32. In the Codename One section select the GUI builder
form
Figure 2.33. Type in the name of the form and click finish, you can
change the type to be a Container or Dialog
76
Figure 2.34. Launch the GUI builder thru the right click menu on the
newly created file
IntelliJ/IDEA
In IntelliJ you need to follow these 3 steps:
Figure 2.35. Right click the package select New Codename One
Form (or Dialog/Container)
Figure 2.37. Launch the GUI builder thru the right click menu on the
newly created file
Eclipse
In Eclipse you need to follow these 4 steps:
78
Figure 2.39. In the Codename One section select the GUI builder
option
79
Figure 2.40. Type in the name of the form and click finish, you can
change the type to be a Container or Dialog
Figure 2.41. Launch the GUI builder thru the right click menu on the
newly created file
Basic Usage
Notice that the UI of the new GUIBuilder might change in various ways but the basic
concepts should remain the same.
80
81
Figure 2.44. You can drag any component you want from the
sidebar to the main UI
You can then re-arrange the order of the components but since they use the default
FlowLayout you cant position them anywhere you want. Well discuss arrangement
and layout managers in the GUI builder below.
You should have a UI that looks like this when you select the button you placed, it
shows the properties that you can modify and the events that you can bind:
82
Figure 2.46. You can edit properties such as the icon property by
clicking it, this opens the image selection dialog
You can add an image to the resource file using the designer tool
46
as covered in this video
For things like setting the text on the component we can use a convenient "long click"
on the component to edit the text in place as such:
Figure 2.47. Use the long click to edit the text "in place"
46
http://www.codenameone.com/how-do-i---fetch-an-image-from-the-resource-file---add-a-
multiimage.html
83
Events
When a component supports broadcasting events you can bind such events by
selecting it, then selecting the events tab and clicking the button matching the event
type
Figure 2.48. The events tab is listed below supported event types
can be bound above
Once an event is bound the IDE will open to the event code e.g.:
84
Some IDEs only generate the project source code after you explicitly
build the project so if your code needs to access variables etc. try
building first
Within the code you can access all the GUI components you defined with the gui_
prefix e.g. Button_1 from the UI is represented as:
private com.codename1.ui.Button gui_Button_1 = new
com.codename1.ui.Button();
Layouts
In this section we wont try to discuss layouts in depth as this is a deep and complex
subject. You can read more about the properties of the various Codename One layouts
47
in the developer guide .
In general layouts define the mathematical logic for component positions that we can
then apply to the various resolutions supported by the devices. If we didnt have layouts
the UI wouldnt fit on the multitude of devices where it should work. You can nest layouts
by placing containers within the UI and giving any container a different layout manager,
this allows you to construct very elaborate layouts.
You can pick a layout manager using this UI:
47
https://www.codenameone.com/manual/basics.html
85
86
87
88
Underlying XML
Saving the project generates an XML file representing the UI into the res directory in
the project, the GUI file is created in a matching hierarchy in the project under the res/
guibuilder directory:
89
90
This format is relatively simple and is roughly the same format used by the old GUI
builder which makes the migration to the new GUI builder possible. This file triggers
the following Java source file:
package com.mycompany.myapp;
/**
* GUI builder created Form
*
* @author shai
*/
public class MyForm extends com.codename1.ui.Form {
public MyForm() {
}
this(com.codename1.ui.util.Resources.getGlobalResources());
initGuiBuilderComponents(resourceObjectInstance);
com.codename1.ui.Label();
com.codename1.ui.Button();
gui_Button_1.addActionListener(callback);
com.codename1.ui.events.ActionListener,
com.codename1.ui.events.DataChangedListener {
this.cmp = cmp;
91
ev) {
com.codename1.ui.Component sourceComponent =
ev.getComponent();
if(sourceComponent.getParent().getLeadParent() != null) {
sourceComponent =
sourceComponent.getParent().getLeadParent();
}
if(sourceComponent == gui_Button_1) {
onButton_1ActionEvent(ev);
resourceObjectInstance) {
guiBuilderBindComponentListeners();
setLayout(new com.codename1.ui.layouts.FlowLayout());
setTitle("My new title");
setName("MyForm");
addComponent(gui_Label_1);
addComponent(gui_Button_1);
gui_Label_1.setText("Hi World");
gui_Label_1.setName("Label_1");
gui_Button_1.setText("Click Me");
gui_Button_1.setName("Button_1");
}// </editor-fold>
ev) {
}
}
92
93
Chapter3.Theme Basics
This chapter covers the creation of a simple hello world style theme and its visual
customization. It uses the Codename One Designer tool to demonstrate basic concepts
in theme creation such as 9-piece borders, selectors and style types.
A theme initializes the Style objects, which are then used by the components to render
3
4
themselves or by the LookAndFeel & DefaultLookAndFeel classes to create the
appearance of the application.
Codename One themes have some built-in defaults, e.g. borders for buttons and
padding/margin/opacity for various components. These are a set of common sense
defaults that can be overridden within the theme.
5
Codename One themes are effectively a set of UIIDs mapped to a Style object.
Codename One applications always have a theme, you can modify it to suit your needs
and you can add multiple themes within the main resource file.
You can also add multiple resource files to a project and work with them. In code a
theme is initialized using this code in your main class:
private Resources theme;
public void init(Object context) {
}
theme = UIManager.initFirstTheme("/theme");
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html
2
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html
3
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/LookAndFeel.html
4
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/DefaultLookAndFeel.html
5
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html
94
Theme Basics
The initFirstTheme method is a helper method that hides some try / catch
logic as well as some verbosity. This could be expressed as:
try {
theme = Resources.openLayered("/theme");
UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()
[0]));
} catch(IOException e){
}
e.printStackTrace();
In GUI builder application themes get loaded internally by the state machine. You can
override their loading via the initTheme method, this is discussed in the old GUI
builder section.
The paragraph above refers to the old GUI builder. The new GUI
builder which is currently in alpha stages uses the standard manual
application structure.
95
Theme Basics
96
Theme Basics
There are several interesting things to notice here the preview section allows us to
instantly see the changes we make to the theme data.
Theme Basics
Selected - when the component has focus or is being touched you will see this style.
Notice that on touch devices focus is only shown when the screen is touched.
Pressed - only applies to buttons, tabs. Represents the look of a component when
its pressed.
Disabled - used when the developer has invoked setEnabled(false) on the
component. This indicates the component is inaccessible.
Figure 3.4. You can use these tabs to add the various types of styles
and theme constants
The most important section is the style section. It allows us to add/edit/remove style
UIIDs.
Notice the Default Style section, it allows us to customize global defaults for the styles.
Use it with caution as changes here can have wide implications.
98
Theme Basics
Figure 3.5. The theme selection area allows us to add, edit and
delete entries. Notice the default style entry which is a unique
special case
When we add an entry to the style we can just type the desired UIID into the box at
the top of the dialog. We can also pick a UIID from the combo box but that might not
include all potential options.
You can use the Component Inspector tool in the simulator to locate
6
a component and its UIID in a specific Form .
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
99
Theme Basics
Figure 3.6. When pressing the Add/Edit entry we can edit a specific
style entry UIID
When we add/edit an entry an important piece of the puzzle is the Derive check box that
appears next to all of the UIID entries. All styles derive from the base style and usually
from the native theme defaults, so when this flag is checked the defaults will be used.
When you uncheck that checkbox the fields below it become editable and you can
override the default behavior. To restore the default just recheck that flag.
A common oddity for developers is that when they press Add and
dont derive any entry nothing is actually added. The entries in
the theme are essentially key/value pairs so when you dont add
anything there are no keys so the entry doesnt show up.
Theme Basics
The Title is surrounded by a TitleArea container that encloses it, above the title
you will also see the StatusBar UIID that prevents the status details from drawing
on top of the title text.
Theme Basics
3. Background Color/Transparency - If transparency is larger than 0 then this takes
effect.
Gradients sit at a higher position than background color. However,
their performance is abysmal at the time of this writing and we
strongly recommend avoiding their usage.
102
Theme Basics
while
Theme Basics
104
Theme Basics
105
Theme Basics
106
Theme Basics
107
Theme Basics
108
Theme Basics
109
Theme Basics
110
Theme Basics
111
Theme Basics
112
Theme Basics
3.3.4.Alignment
Not all component types support alignment and even when they do they dont support it
7
for all elements. E.g. a Label and its subclasses support alignment but will only apply
it to the text and not the icon.
8
Notice that Container doesnt support alignment. You should use the layout manager
to tune component positioning.
7
8
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
113
Theme Basics
114
Theme Basics
3.3.6.Borders
Borders are a big subject in their own right, the UI for their creation is also a bit
confusing:
115
Theme Basics
For your convenience you can create a rudimentary image with the create image stage
but for a professional looking application you would usually want to use a design by
a professional designer.
116
Theme Basics
Figure 3.33. Stage 2: Cutting the image and adapting it to the DPIs
The second stage is probably the hardest and most important one in this wizard!
You can change the values of the top/bottom/left/right spinners to move the position of
the guide lines that indicate the various 9 pieces. The image shows the correct cut for
this image type with special attention to the following:
The left/right position is high enough to fit in the rounded corners in their entirety.
Notice that we didnt just leave 1 pixel as that performs badly, we want to leave as
much space as possible!
The top and bottom lines have exactly one pixel between them. This is to avoid
breaking the gradient. E.g. if we set the lines further apart we will end up with this:
Figure 3.34. This is why its important to keep the lines close when
a gradient is involved, notice the tiling effect
117
Theme Basics
Figure 3.35. When the lines are close together the gradient effect
grows more effectively
The elements on the right hand side include the Generate Multi Image options. Here
you can indicate the density of the source image you are using (e.g. if its for iPhone
5 class device pick Very High). You can then select in the checkboxes below the
densities that should be generated automatically for you. This allows fine detail on
the border to be maintained in the various high/low resolution devices.
We go into a lot of details about multi images in the advanced
theming section.
118
Theme Basics
Theme Basics
3 Image Mode
The 9-piece border has a (rarely used) special case: 3 image mode. In this mode
a developer can specify the top left corner, the top image and the center image to
produce a 9 piece border. The corner and top piece are then rotated dynamically
to produce a standard 9-piece border on the device.
This is useful for reducing application code size but isnt used often as it requires
a more symetric UI.
Dont confuse the 3-image mode for the 9-piece border with
the horizontal/vertical image border below
120
Theme Basics
Figure 3.39. Horizontal image border is commonly used for UIs that
cant grow vertically e.g. the iOS style back button
The horizontal and vertical image borders accept 3 images of their respective AXIS
and build the border by placing one image on each side and tiling the center image
between them. E.g. A horizontal border will never grow vertically.
9
Languages that are written from right to left such as Hebrew, Arabic etc.
121
Theme Basics
3.3.9.Empty Border
Empty borders enforce the removal of a border. This is important if you would like to
block a base style from having a border.
10
E.g. Buttons have borders by default. If you would like to create a Button that is
strictly of solid color you could just define the border to be empty and then use the solid
color as you see fit.
There is a null border which is often confused with an empty border.
You should use empty border and not null border.
3.3.10.Bevel/Etched Borders
We generally recommend avoiding bevel/etched border types as they arent as
efficient and look a bit dated in todays applications. We cover them here mostly for
completeness.
3.3.11.Derive
Derive allows us to inherit the behavior of a UIID and extend it with some customization.
10
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
122
Theme Basics
E.g. Lets say we created a component thats supposed to look like a title, we could do
something like:
cmp.setUIID("Title");
But title might sometimes be aligned to the left (based on theme) and we always want
our component to be center aligned. However, we dont want that to affect the actual
titles in the app
To solve this we can define a MyTitle UIID and derive the Title UIID. Then just
customize that one attribute.
123
Theme Basics
3.3.12.Fonts
Codename One currently supports 3 font types:
System fonts - these are very simplistic builtin fonts. They work on all platforms and
come in one of 3 sizes. However, they are ubiquitous and work in every platform
in all languages.
TTF files - you can just place a TTF file in the src directory of the project and it will
appear in the True Type combo box.
Native fonts - these arent supported on all platforms but generally they allow you to
use a set of platform native good looking fonts. E.g. on Android the devices Roboto
font will be used and on iOS Helvetica Neue will be used.
If you use a TTF file MAKE SURE not to delete the file when there
MIGHT be a reference to it. This can cause hard to track down
issues!
Notice that a TTF file must have the ".ttf" extension, otherwise the
build server wont be able to recognize the file as a font and set it up
accordingly (devices need fonts to be defined in very specific ways).
Once you do that, you can use the font from code or from the theme.
Theme Basics
You can size native/TTF fonts either via pixels, millimeters or based on the size of the
equivalent system fonts:
1. System font size - the truetype font will have the same size as a small, medium
or large system font. This allows the developer to size the font based on the device
DPI.
2. Millimeter size - allows sizing the font in a more DPI aware size.
3. Pixels - useful for some unique cases, but highly problematic in multi-DPI
scenarios.
You should notice that font sizing is very inconsistent between
platforms.
To use fonts from code, you can use:
if(Font.isTrueTypeFileSupported()) {
Notice that, in code, only pixel sizes are supported, so its up to you to decide how to
convert that. You also need to derive the font with the proper size, unless you want a
0 sized font which probably isnt very useful.
The font name is the difficult bit, iOS requires the name of the font in order to load the
font. This font name doesnt always correlate to the file name making this task rather
"tricky". The actual font name is sometimes viewable within a font viewer. It isnt always
intuitive, so be sure to test that on the device to make sure you got it right.
due to copyright restrictions we cannot distribute Helvetica and thus
cant simulate it. In the simulator you will see Roboto and not the
device font unless you are running on a Mac.
The code below demonstrates all the major fonts available in Codename One with the
handlee ttf file posing as a standin for arbitrary TTF:
private Label createForFont(Font fnt, String s) {
Label l = new Label(s);
l.getUnselectedStyle().setFont(fnt);
return l;
125
Theme Basics
}
public void showForm() {
Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
Font largePlainSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_PLAIN, Font.SIZE_LARGE);
Font smallBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_BOLD, Font.SIZE_SMALL);
Font mediumBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_BOLD, Font.SIZE_MEDIUM);
Font largeBoldSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_BOLD, Font.SIZE_LARGE);
Font smallItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_ITALIC, Font.SIZE_SMALL);
Font mediumItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_ITALIC, Font.SIZE_MEDIUM);
Font largeItalicSystemFont = Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_ITALIC, Font.SIZE_LARGE);
Font smallPlainMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN,
Font.SIZE_SMALL);
Font mediumPlainMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN,
Font.SIZE_MEDIUM);
Font largePlainMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN,
Font.SIZE_LARGE);
Font smallBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE,
Font.STYLE_BOLD, Font.SIZE_SMALL);
126
Theme Basics
Font mediumBoldMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_MEDIUM);
Font largeBoldMonospaceFont = Font.createSystemFont(Font.FACE_MONOSPACE,
Font.STYLE_BOLD, Font.SIZE_LARGE);
Font smallItalicMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC,
Font.SIZE_SMALL);
Font mediumItalicMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC,
Font.SIZE_MEDIUM);
Font largeItalicMonospaceFont =
Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_ITALIC,
Font.SIZE_LARGE);
Font smallPlainProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN,
Font.SIZE_SMALL);
Font mediumPlainProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_MEDIUM);
Font largePlainProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_LARGE);
Font smallBoldProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_SMALL);
Font mediumBoldProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_MEDIUM);
Font largeBoldProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_LARGE);
Font smallItalicProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_SMALL);
Font mediumItalicProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_MEDIUM);
Font largeItalicProportionalFont =
Font.createSystemFont(Font.FACE_PROPORTIONAL,
Font.SIZE_LARGE);
Font.STYLE_PLAIN,
Font.STYLE_PLAIN,
Font.STYLE_BOLD,
Font.STYLE_BOLD,
Font.STYLE_BOLD,
Font.STYLE_ITALIC,
Font.STYLE_ITALIC,
Font.STYLE_ITALIC,
String[] nativeFontTypes = {
127
Theme Basics
add(createForFont(smallPlainMonospaceFont, "smallPlainMonospaceFont")).
add(createForFont(mediumPlainMonospaceFont, "mediumPlainMonospaceFont")).
add(createForFont(largePlainMonospaceFont, "largePlainMonospaceFont")).
add(createForFont(smallBoldMonospaceFont, "smallBoldMonospaceFont")).
add(createForFont(mediumBoldMonospaceFont, "mediumBoldMonospaceFont")).
add(createForFont(largeBoldMonospaceFont, "largeBoldMonospaceFont")).
add(createForFont(smallItalicMonospaceFont, "smallItalicMonospaceFont")).
add(createForFont(mediumItalicMonospaceFont, "mediumItalicMonospaceFont")).
add(createForFont(largeItalicMonospaceFont, "largeItalicMonospaceFont")).
128
Theme Basics
add(createForFont(smallPlainProportionalFont, "smallPlainProportionalFont")).
add(createForFont(mediumPlainProportionalFont, "mediumPlainProportionalFont")).
add(createForFont(largePlainProportionalFont, "largePlainProportionalFont")).
add(createForFont(smallBoldProportionalFont, "smallBoldProportionalFont")).
add(createForFont(mediumBoldProportionalFont, "mediumBoldProportionalFont")).
add(createForFont(largeBoldProportionalFont, "largeBoldProportionalFont")).
add(createForFont(smallItalicProportionalFont, "smallItalicProportionalFont")).
add(createForFont(mediumItalicProportionalFont, "mediumItalicProportionalFont")).
add(createForFont(largeItalicProportionalFont, "largeItalicProportionalFont"));
hi.show();
129
Theme Basics
130
Theme Basics
Figure 3.45. The same demo running on a OnePlus One device with
Android 5.1
Font Effects
You can define an effect to be applied to a specific font, specifically:
Underline
Strike thru
3d text raised/lowered
3d shadow north
The "3d" effects effectively just draw the text twice, with a sligh offest and two different
colors to create a "3d" feel.
All of the effects are relatively simple and performant.
131
Chapter4.Advanced Theming
This chapter covers the advanced concepts of theming as well as deeper understanding
of how to build/design a Codename One Theme. :icons: font
are
UIIDs can be customized via the GUI builder and allows for powerful customization of
individual components.
The class name of the component is commonly the same as the
UIID, but they are in essence separate entities.
4.2.Theme Layering
There are two use cases in which you would want to use layering:
You want a slightly different theme in one platform
You want the ability to customize your theme for a specific use case, e.g. let a user
select larger fonts
1
https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html
132
Advanced Theming
This is actually pretty easy to do and doesnt require re-doing the entire theme. You can
do something very similar to the cascading effect of CSS where a theme is applied "on
top" of another theme. To do that just add a new theme using the Add Theme button.
Make sure to remove the includeNativeBool constant in the
new theme!
In the new theme define the changes e.g. if you just want a larger default font define
only that property for all the relevant UIIDs and ignore all other properties!
For a non-gui builder app the theme loading looks like this by default:
theme = UIManager.initFirstTheme("/theme");
This assumes the name of your main theme is "Theme" (not the layer
theme you just added).
The original code relies on the theme being in the 0 position in the theme name array
which might not be the case!
When you want to add the theme layer just use:
UIManager.getInstance().addThemeProps(theme.getTheme("NameOfLayerTheme"));
The addThemeProps call will layer the secondary theme on top of the primary
"Theme" and keep the original UIIDs defined in the "Theme" intact.
If you apply theme changes to a running application you can use Forms
`refreshTheme() to update the UI instantly and provide visual feedback for the
theme changes.
Advanced Theming
Codename One allows you to override a resource for a specific platform when doing this
you can redefine a resource differently for that specific platform and also add platform
specific resources.
134
Advanced Theming
4.4.Theme Constants
The Codename One Designer has a tab for creating constants which can be used
to add global values of various types and behavior hints to Codename One and its
components. Constants are always strings, there are some conventions that allow the
UI to adapt to input various types more easily e.g. if a constant ends with the word Bool it
is treated as a boolean (true/false) value and will be displayed as a checkbox. Similarly
Int will display a numeric picker and Image will show a combo box to pick an image.
The combo box in the designer for adding a theme constant is
editable, you can just type in any value you want!
2
To use a constant one can use the UIManager 's methods to get the appropriate
constant type specifically:
2
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html
135
Advanced Theming
getThemeConstant
isThemeConstant
getThemeImageConstant
Internally, Codename One has several built in constants and the list is constantly
growing. As we add features to Codename One, we try to keep this list up to date but
the very nature of theme constants is "adhoc" and some might not make it here.
Description/Argument
alwaysTensileBool
backGestureThresholdInt
backUsesTitleBool
defaultCommandImage
dialogButtonCommandsBool
dialogBlurRadiusInt
dialogPosition
centeredPopupBool
changeTabOnFocusBool
https://www.codenameone.com/javadoc/com/codename1/ui/util/SwipeBackSupport.html
136
Advanced Theming
Constant
Description/Argument
checkBoxCheckDisImage
checkBoxCheckedImage
checkBoxOppositeSideBool
checkBoxUncheckDisImage
checkBoxUncheckedImage
comboImage
commandBehavior
ComponentGroupBool
dialogTransitionIn
dialogTransitionInImage
dialogTransitionOut
4
5
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Timeline.html
137
Advanced Theming
Constant
Description/Argument
defaultCommandImage
defaultEmblemImage
dialogTransitionOutImage
disabledColor
dlgButtonCommandUIID
dlgCommandButtonSizeInt
dlgCommandGridBool
dlgInvisibleButtons
dialogs
dlgSlideDirection
Slide hints
dlgSlideInDirBool
Slide hints
dlgSlideOutDirBool
Slide hints
drawMapPointerBool
fadeScrollBarBool
fadeScrollEdgeBool
6
7
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Timeline.html
138
Advanced Theming
Constant
Description/Argument
scroll until we reach the edge (common
on Android)
fadeScrollEdgeInt
firstCharRTLBool
Indicates to the
8
GenericListCellRenderer that it should
determine RTL status based on the first
character in the sentence
noTextModeBool
fixedSelectionInt
formTransitionIn
formTransitionInImage
formTransitionOut
formTransitionOutImage
globalToobarBool
hideBackCommandBool
hideEmptyTitleBool
hideLeftSideMenuBool
10
12
8
https://www.codenameone.com/javadoc/com/codename1/ui/list/GenericListCellRenderer.html
9
https://www.codenameone.com/javadoc/com/codename1/ui/List.html
10
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
11
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Timeline.html
12
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
13
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Timeline.html
139
Advanced Theming
Constant
Description/Argument
ignorListFocusBool
infiniteImage
includeNativeBool
listItemGapInt
listLongPressBool
mapTileLoadingImage
mapTileLoadingText
mapZoomButtonsBool
mediaBackImage
mediaFwdImage
mediaPauseImage
mediaPlayImage
menuButtonBottomBool
14
15
https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html
https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html
140
Advanced Theming
Constant
Description/Argument
menuButtonTopBool
menuHeightPercent
menuImage
menuImageSize
menuPrefSizeBool
menuSlideDirection
menuSlideInDirBool
menuSlideOutDirBool
menuTransitionIn
menuTransitionInImage
menuTransitionOut
menuTransitionOutImage
menuWidthPercent
minimizeOnBackBool
onOffIOSModeBool
otherPopupRendererBool
16
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
141
Advanced Theming
Constant
Description/Argument
PackTouchMenuBool
paintsTitleBarBool
popupCancelBodyBool
PopupDialogArrowBool
PopupDialogArrowBottomImage
PopupDialogArrowTopImage
PopupDialogArrowLeftImage
PopupDialogArrowRightImage
popupNoTitleAddPaddingInt
popupTitleBool
pullToRefreshImage
pureTouchBool
142
Advanced Theming
Constant
Description/Argument
radioOppositeSideBool
radioSelectedDisImage
radioSelectedImage
radioUnselectedDisImage
radioUnselectedImage
releaseRadiusInt
rendererShowsNumbersBool
reverseSoftButtonsBool
rightSideMenuImage
rightSideMenuPressImage
showBackCommandOnTitleBool
shrinkPopupTitleBool
sideMenuAnimSpeedInt
sideMenuFoldedSwipeBool
sideMenuImage
sideMenuPressImage
17
17
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
143
Advanced Theming
Constant
Description/Argument
sideMenuShadowBool
sideMenuShadowImage
sideMenuSizeTabPortraitInt
sideMenuSizePortraitInt
sideMenuSizeTabLandscapeInt
sideMenuSizeLandscapeInt
sideMenuTensileDragBool
sideSwipeActivationInt
sideSwipeSensitiveInt
slideDirection
slideInDirBool
slideOutDirBool
sliderThumbImage
snapGridBool
statusBarScrollsUpBool
switchButtonPadInt
144
Advanced Theming
Constant
Description/Argument
switchMaskImage
switchOnImage
switchOffImage
TabEnableAutoImageBool
TabSelectedImage
TabUnselectedImage
tabPlacementInt
tabsSlideSpeedInt
tabsFillRowsBool
tabsGridBool
tabsOnTopBool
textCmpVAlignInt
18
https://www.codenameone.com/javadoc/com/codename1/ui/Tabs.html
145
Advanced Theming
Constant
Description/Argument
textFieldCursorColorInt
tickerSpeedInt
tintColor
topMenuSizeTabPortraitInt
topMenuSizePortraitInt
topMenuSizeTabLandscapeInt
topMenuSizeLandscapeInt
touchCommandFillBool
touchCommandFlowBool
transitionSpeedInt
treeFolderImage
treeFolderOpenImage
treeNodeImage
tensileDragBool
19
https://www.codenameone.com/javadoc/com/codename1/ui/tree/Tree.html
146
19
class
Advanced Theming
4.5.Native Theming
Codename One uses a theme constant called includeNativeBool , when that
constant is set to true Codename One starts by loading the native theme first and
then applying all the theme settings. This effectively means your theme "derives" the
style of the native theme first, similar to the cascading effect of CSS. Internally this is
exactly what the theme layering section covered.
By avoiding this flag you can create themes that look EXACTLY the same on all
platforms.
If you avoid the native theming you you might be on your own. A few
small device oddities such as the iOS status bar are abstracted by
native theming. Without it you will need to do everything from scratch.
You can simulate different OS platforms by using the native theme menu option
147
Advanced Theming
20
UIID to white.
21
22
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
21
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
22
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html
23
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html
24
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html#refreshTheme--
148
Advanced Theming
refreshTheme() is very expensive we recommend that you dont
use it unless you really need to
A theme Hashtable key is comprised of:
[UIID.][type#]attribute
The UIID, corresponds to the components UIID e.g. Button , CheckBox
25
etc. It is
The type is omitted for the default unselected type, and may be one of sel (selected
type), dis (disabled type) or press (pressed type). The attribute should be one of:
derive - the value for this attribute should be a string representing the base
component.
bgColor - represents the background color for the component, if applicable, in a
web hex string format RRGGBB e.g. ff0000 for red.
fgColor - represents the foreground color, if applicable.
border - an instance of the border class, used to display the border for the
component.
bgImage - an Image
26
27
object instance.
one
of
the
149
Advanced Theming
backgroundGradient - contains an Object array containing 2 integers for the
colors of the gradient. If the gradient is radial it contains 3 floating points defining
the x, y & size of the gradient.
So to set the foreground color of a selected button to red, a theme will define a property
like:
Button.sel#fgColor=ff0000
This information is mostly useful for understanding how things work within Codename
One, but it can also be useful in runtime.
E.g. to increase the size of all fonts in the application, we can do something like:
Hashtable h = new Hashtable();
h.put("font", largeFont);
UIManager.getInstance().addThemeProps(h);
Display.getInstance().getCurrent().refreshTheme();
Advanced Theming
A first generation the iPad 2 device had a 160 PPI (160 pixels per inch density).
The much smaller iPhone 4 from the same era had 320 PPI and modern devices
already exceed 600+ PPI values. The contrast is staggering especially when
compared to the desktop.
Mobile UIs are expected to use all available pixels to their full extent. In that
sense when an application runs on a tablet you dont want it to just provide a
larger image for the icons but rather have it cram more information into a single
form. So we need to rethink image sizing not just in pixels but in millimeters/
inches.
The density of the devices varies significantly and Codename One tries to simplify the
process by unifying everything into one set of values to indicate density. For simplicitys
sake, density is sometimes expressed in terms of pixels, however it is mapped internally
to actual screen measurements where possible.
A multi-image is an image that has multiple varieties for different densities, and thus
looks sharp in all the densities. Since scaling on the device cant interpolate the
data (due to performance considerations), significant scaling on the device becomes
impractical. However, a multi-image will just provide the right resolution image for the
given device type.
From the programming perspective this is mostly seamless, a developer just accesses
one image and has no ability to access the images in the different resolutions. Within the
designer, however, we can explicitly define images for multiple resolutions and perform
high quality scaling so the right image is available.
We can use two basic methods to add a multi-image: quick add & standard add.
Both methods rely on understanding the source resolution of the image, e.g. if you have
an icon that you expect to be 128x128 pixels on iPhone 4, 102x102 on nexus one and
64x64 on iPhone 3gs. You can provide the source image as the 128 pixel image and
just perform a quick add option while picking the Very High density option.
This will indicate to the algorithm that your source image is designed for the "very high"
density and it will scale for the rest of the densities accordingly.
This relies on the common use case of asking your designer to
design for one high end device (e.g. iPhone 6+) then you can just
take the resources and add them as "HD" resources and they will
automatically adapt to the lower resolutions.
151
Advanced Theming
Alternatively, you can use the standard add multi-image dialog and set it like this:
Notice that we selected the square image option, essentially eliminating the height
option. Setting values to 0 prevents the system from generating a multi-image entry
for that resolution, which will mean a device in that category will fall on the closest
alternative.
The percentage value will change the entire column, and it means the percentage of
the screen. E.g. We know the icon is 128 for the very high resolution, we can just move
the percentage until we reach something close to 128 in the Very High row and the
other rows will represent a size that should be pretty close in terms of physical size
to the 128 figure.
At runtime, you can always find the host devices approximate pixel density using the
Display.getDeviceDensity() method. This will return one of:
Density
Example Device
Display.DENSITY_VERY_LOW
~ 88 ppi
152
Advanced Theming
Display.DENSITY_LOW ~ 120 ppi
Display.DENSITY_MEDIUM
~ 160 ppi
Display.DENSITY_VERY_HIGH
~ 320 ppi
Display.DENSITY_HD
~ 540 ppi
~ 1250ppi
4.8.1.Fractions of Millimeters
Sometimes millimeters dont give you enough precision for what you want to do.
Currently the designer only allows you to specify integer values for most units.
However, you can achieve more precise results when working directly in Java. The
Display.convertToPixels() method will allow you to convert millimeters (or
DIPS) to pixels. It also only takes an integer input, but you can use it to obtain a multiplier
that you can then use to convert any millimeter value you want into pixels.
E.g.
double pixelsPerMM = ((double)Display.getInstance().convertToPixels(10,
true)) / 10.0;
And now you can set the padding on an element to 1.5mm. E.g.
153
Advanced Theming
myButton.getAllStyles().setPaddingUnit(Style.UNIT_TYPE_PIXELS);
int pixels = (int)(1.5 * pixelsPerMM);
29
30
31
http://freebiesbug.com/psd-freebies/iphone-6-ui-kit/
https://dribbble.com/adrianchiran
https://www.codenameone.com/files/iOS_UI-Kit.psd
154
29
30
Advanced Theming
155
Advanced Theming
2. Re-create the general structure and layout of the design in a Codename One Form
using nested components and layout managers. Here is a break-down of how we
structured the component hierarchy in the Form :
156
Advanced Theming
Advanced Theming
Figure 4.7. Selecting a layer from the region you are interested in
Scroll up the hierarchy a bit and uncheck/recheck the eye icon on the left until you
locate the right element layer.
158
Advanced Theming
Figure 4.8. Selecting a layer from the region you are interested in
Right click the layer and select Convert To Smart Object.
The right click menu will present different options when you click
different areas of the layer, clicking on the left area of the layer works
159
Advanced Theming
Figure 4.9. In the right click menu option select "Convert To Smart
Object"
Once the layer hierarchy is a smart object you can just double click it which will open
the sub hierarchy in a new tab and you now only have the pieces of the image you
care about.
160
Advanced Theming
Figure 4.10. Double clicking the smart object allows us to edit only
the form we need
161
Advanced Theming
Figure 4.11. The text tool allows us to inspect the font used
Then I can double click the text area layer to find out the font in the top of the UI like this:
162
Advanced Theming
Also notice that the color of the font is accessible in that toolbar, by clicking the color
element we get this dialog which shows the color value to be f73267, this is something
we will use later
Figure 4.13. The color dialog lists the hex color at the bottom, we
can paste that directly to the designer tool
We can now hide both text layers so they wont pose a problem later.
163
Advanced Theming
Figure 4.15. The canvas size dialog for the camera.png file
164
Advanced Theming
We can now use File Save As and save the first image resource we will need into a
temporary directory. Make sure to save a PNG file to preserve quality and transparency!
For convenience well refer to the file as camera.png when we need it later.
165
Advanced Theming
Figure 4.18. The select tool and the clean image we want to select
Now drag the select tool to select the image dont cross into the white pixels below the
image. You can use the zoom value and set it to a very high value to get the selection
right.
When the selection is right click Edit Copy Merged. Normally Copy would only copy
a specific layer but in this case we want to copy what we see on the screen!
Now click File New it should have the Presets set to Clipboard which means the
newly created image is based on what we just copied (that is seriously great UX). Just
accept that dialog and paste (Ctrl-V or Command-V).
You can now save the image, since its just a background using JPEG is totally
acceptable in this case. We named it background.jpg .
166
Advanced Theming
Figure 4.20. The eye drop tool can be pointed at an area of the image
to get the color in that region
167
Advanced Theming
4.9.2.The Code
While that was verbose it was relatively simple. Well create a simple barebones manual
application with the native theme.
The reason for this is to avoid "noise", if we use a more elaborate
theme it would have some existing settings. This can make the
tutorial harder to follow
select Images Quick Add Multi Images. Select the 3 images we created above:
background.jpg , camera.png & camera-button.png . Leave the default
setting on Very High and press OK.
Then save the resource file so we can use these images from code.
Here is the source code we used to work with the UI above there are comments within
the code explaining some of the logic:
private Label createSeparator() {
Label sep = new Label();
sep.setUIID("Separator");
168
Advanced Theming
// the separator line
// are hidden when they have no content, this method disables that
behavior
sep.setShowEvenIfBlank(true);
return sep;
if(current != null){
current.show();
return;
// we create 4mm material arrow images for the back button and the Get
started button
Style iconStyle =
psdTutorial.getUIManager().getComponentStyle("Title");
FontImage leftArrow =
FontImage.createMaterial(FontImage.MATERIAL_ARROW_BACK, iconStyle, 4);
FontImage rightArrow =
FontImage.createMaterial(FontImage.MATERIAL_ARROW_FORWARD, iconStyle, 4);
// we place the back and done commands in the toolbar, we need to
// with the camera icon on top. This is all wrapped in the title
169
Advanced Theming
// the label will preserve the original size of the image without
new Label(theme.getImage("camera-button.png")),
cameraButton);
cameraButton.setUIID("CameraButton");
Container titleContainer = Container.encloseIn(
new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER),
cameraLayer, BorderLayout.CENTER);
titleContainer.setUIID("TitleContainer");
TextField.EMAILADDR);
TextField.PASSWORD);
TextField.PHONENUMBER);
// The phone and full name have vertical separators, we use two table
fullName.add(fullNameLayout.createConstraint().widthPercentage(49),
firstName).
add(fullNameLayout.createConstraint().widthPercentage(1),
createSeparator()).
add(fullNameLayout.createConstraint().widthPercentage(50),
lastName);
Container fullPhone = TableLayout.encloseIn(3, phonePrefix,
createSeparator(), phone);
// The button in the south portion needs the arrow icon to be on the
170
Advanced Theming
// Y container which we allow to scroll. BorderLayout Containers
Container by = BoxLayout.encloseY(
fullName,
createSeparator(),
email,
createSeparator(),
password,
createSeparator(),
fullPhone,
createSeparator()
);
by.setScrollableY(true);
psdTutorial.add(BorderLayout.NORTH, titleContainer).
add(BorderLayout.SOUTH, southButton).
add(BorderLayout.CENTER, by);
psdTutorial.show();
4.9.3.Styling The UI
So the code above is most of the work but we still need to put everything together using
the theme. This is what we have so far:
171
Advanced Theming
Figure 4.22. Before applying the changes to the theme this is what
we have
172
Advanced Theming
Advanced Theming
Right - 3 millimeter
Top - 8 millimeter
Bottom - 2 millimeter
This will allow enough space for the title. Define margin as 0 on all sides. Then press
OK.
Add the "Title" UIID. In the Color tab define the foreground as ffffff define
transparency as 0 (fully transparent so we will see the TitleContainer ). Define
padding as 1 millimeter on all sides and margin as 0 on all sides.
In the Border tab press the button and select [Empty].
In the Font tab select the True Type as native:MainThin. Select the True Type Size as
millimeters and set the value to 3.5 .
Press OK to save the changes.
Copy the Title UIID and paste it, change the name to "TitleCommand" and press
OK to save the changes.
Copy the Title UIID again and paste it, change the name to "RedCommand". In the
Color tab set the foreground color to f73267 . In the Font tab set the True Type to
native:MainLight and set the size to 3. Press OK to save the changes.
Add the "TitleArea" UIID. In the Color tab define transparency as 0 (fully transparent
so we will see the TitleContainer ). Define padding and margin as 0 on all sides.
In the Border tab press the button and select [Empty]. Press OK to save the changes.
Add the "TextField" UIID. In the Color tab define transparency as 255 (fully opaque)
and the background as ffffff (white). Define padding as 2 millimeter on all sides
and margin as 0 on all sides.
In the Border tab press the button and select [Empty]. In the Font tab set the True
Type to native:MainLight and set the size to 2. Press OK to save the changes.
Copy the TextField UIID again and paste it, change the name to "TextHint". In the
Color tab set the foreground color to 4d606f . Press OK to save the changes.
Add the "SouthButton" UIID. In the Color tab define transparency as 255 (fully opaque)
and the background as f73267 (red) and the foreground as ffffff (white). Define
Alignment as Center.
174
Advanced Theming
175
Chapter5.The Components Of
Codename One
This chapter covers the components of Codename One. Not all components are
1
covered, but it tries to go deeper than the JavaDocs .
5.1.Container
The Codename One container is a base class for many high level components; a
container is a component that can contain other components.
Every component has a parent container that can be null if it isnt within a container at
the moment or is a top-level container. A container can have many children.
5.1.1.Composite Components
Codename One components share a very generic hierarchy of inheritance e.g. Button
3
derives from Label and thus receives all its abilities.
1
2
3
https://www.codenameone.com/javadoc/
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
176
However, some components are composites and derive from the Container class.
5
E.g. the MultiButton is a composite button that derives from Container but acts/
looks like a Button . Normally this is pretty seamless for the developer, with a few
things to keep in mind.
You should not use the Container derived methods on such a composite
component (e.g. add / remove etc.).
You cant cast it to the type that it relates to e.g. you cant cast MultiButton to
Button .
6
Lead Component
Codename One has a rather unique feature for creating composite
components: "lead components". This feature effectively allows components
like MultiButton to act as if they are a single component while really being
comprised of multiple components.
Lead components work by setting a single component within as the "leader" it
determines the style state for all the components in the hierarchy so if we have a
Container that is lead by a Button the button will determine if the selected/
pressed state is returned for the entire container hierarchy.
This creates a case where a single Component has multiple nested UIIDs
e.g. `MultiButton has UIIDs such as `MultiLine1 that can be
9
customized via APIs such as setUIIDLine1 .
4
5
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html
6
https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.html#getSource-7
https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.html#getComponent-8
https://www.codenameone.com/javadoc/com/codename1/ui/events/
ActionEvent.html#getActualComponent-9
https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html#setUIIDLine1java.lang.String-
177
5.2.Form
10
Form is the top-level container of Codename One, Form derives from Container
and is the element we show. Only one form can be visible at any given time. We can
get the currently visible Form using the code:
Form currentForm = Display.getInstance().getCurrent();
A form is a unique container in the sense that it has a title, a content area and optionally
a menu/menu bar area. When invoking methods such as add / remove on a form,
you are in fact invoking something that maps to this:
myForm.getContentPane().add(...);
11
Form is in effect just a Container that has a border layout, its north section is
occupied by the title area and its south section by the optional menu bar. The center
(which stretches) is the content pane. The content pane is where you place all your
components.
10
11
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
178
179
5.3.Dialog
13
A Dialog is a special kind of Form that can occupy only a portion of the screen, it
also has the additional functionality of the modal show method.
When showing a dialog we have two basic options: modeless and modal:
Modal dialogs (the default) block the current EDT thread until the dialog is dismissed
(to understand how they do it, read about invokeAndBlock ).
Modal dialogs are an extremely useful way to prompt the user since the code can
assume the user responded in the next line of execution. This promotes a linear &
intuitive way of writing code.
Modless dialogs return immediately so a call to show such a dialog cant assume
anything in the next line of execution. This is useful for features such as progress
indicators where we arent waiting for user input.
E.g. a modal dialog can be expressed as such:
12
13
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html
180
} else {
}
// user clicked no
Notice that during the show call above the execution of the next line was "paused"
until we got a response from the user and once the response was returned we could
proceed directly.
All usage of Dialog must be within the Event Dispatch Thread (the
default thread of Codename One). This is especially true for modal
dialogs. The Dialog class knows how to "block the EDT" without
blocking it.
To learn more about invokeAndBlock which is the workhorse behind the modal
dialog functionality check out the EDT section.
The Dialog class contains multiple static helper methods to quickly show user
notifications, but also allows a developer to create a Dialog instance, add information
to its content pane and show the dialog.
Dialogs contain a ContentPane just like Form .
When showing a dialog in this way, you can either ask Codename One to position
14
the dialog in a specific general location (taken from the BorderLayout concept for
locations) or position it by spacing it (in pixels) from the 4 edges of the screen.
E.g. you could do something like this to show a simple modal Dialog :
Dialog d = new Dialog("Title");
d.setLayout(new BorderLayout());
14
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
181
d.setLayout(new BorderLayout());
182
183
5.3.1.Styling Dialogs
Its important to style a Dialog using getDialogStyle()
15
or setDialogUIID
16
methods
The reason for this is that the Dialog is really a Form that takes up the whole
screen. The Form that is visible behind the Dialog is rendered as a screenshot. So
customizing the actual UIID of the Dialog wont produce the desired results.
15
16
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html#getDialogStyle-https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html#setDialogUIID-
java.lang.String-
184
185
Not
all
device
types
support
blur
you
can
test
if
your
device
supports
it
using
Display.getInstnace().isGaussianBlurSupported() . If
blur isnt supported the blur setting will be ignored.
Form hi = new Form("Blur Dialog", new BoxLayout(BoxLayout.Y_AXIS));
Dialog.setDefaultBlurBackgroundRadius(8);
Button showDialog = new Button("Blur");
187
Figure 5.7. The blur effect coupled with the OS default tint
It might be a bit hard to notice the blur effect with the tinting so here is the same code
with tinting disabled:
hi.setTintColor(0);
188
Figure 5.8. The blur effect is more pronounced when the tint is
disabled
5.3.3.Popup Dialog
A popup dialog is a common mobile paradigm showing a Dialog that points at a
specific component. Its just a standard Dialog that is shown in a unique way:
Dialog d = new Dialog("Title");
d.setLayout(new BorderLayout());
189
17
18
17
or Rectangle
18
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
https://www.codenameone.com/javadoc/com/codename1/ui/geom/Rectangle.html
190
Then style the PopupDialog UIID with the image for the Dialog itself.
5.4.InteractionDialog
Dialogs in Codename One can be modal or modeless, the former blocks the calling
thread and the latter does not. However, there is another definition to those terms: A
modal dialog blocks access to the rest of the UI while a modeless dialog "floats" on
top of the UI.
In that sense, all dialogs in Codename One are modal; they block the parent form
since they are effectively just forms that show the "parent" in their background.
19
20
InteractionDialog has an API that is very similar to the Dialog API but, unlike dialog,
it never blocks anything. Neither the calling thread nor the UI.
InteractionDialog isnt a Dialog since it doesnt share
the same inheritance hierarchy. However, it acts and "feels" like
a Dialog despite the fact that its just a Container in the
LayeredPane .
19
20
https://www.codenameone.com/javadoc/com/codename1/components/InteractionDialog.html
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html
191
192
193
5.5.Label
Label
21
represents a text, icon or both. Label is also the base class of Button
which in turn is the base class for RadioButton & CheckBox . Thus the functionality
of the Label class extends to all of these components.
Label text can be positioned in one of 4 locations as such:
Label left = new Label("Left", icon);
left.setTextPosition(Component.LEFT);
top.setTextPosition(Component.TOP);
hi.add(left).add(right).add(bottom).add(top);
21
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
194
22
String width is the real expensive part here, the complexity of font kerning and the recursion required to
195
24
TextArea defaults to multi-line input and TextField defaults to single line input
but both can be used in both cases. The main differences between TextField and
TextArea are:
Blinking cursor is rendered on TextField only
DataChangeListener
25
26
Different UIID
The semantic difference between TextField & TextArea dates
back to the ancestor of Codename One: LWUIT. Feature phones
dont have proper in-place editing capabilities & thus TextField
was introduced to allow such input.
Because it lacks the blinking cursor capability TextArea is often used as a multi-line
label and is used internally in SpanLabel , SpanButton etc.
A common use case is to have an important text
component in edit mode immediately as we enter a Form .
Codename One forms support this exact use case thru the
Form.setEditOnShow(TextArea)
27
method.
TextField & TextArea support constraints for various types of input such as
NUMERIC , EMAIL , URL , etc. Those usually affect the virtual keyboard used, but
23
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
24
https://www.codenameone.com/javadoc/com/codename1/ui/TextArea.html
25
https://www.codenameone.com/javadoc/com/codename1/ui/events/DataChangedListener.html
26
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html#setDoneListenercom.codename1.ui.events.ActionListener27
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html#setEditOnShowcom.codename1.ui.TextArea-
196
int spanButton = 2;
if(Display.getInstance().isTablet()) {
tl = new TableLayout(7, 2);
} else {
}
tl.setGrowHorizontally(true);
hi.setLayout(tl);
TextField firstName = new TextField("", "First Name", 20, TextArea.ANY);
TextField surname = new TextField("", "Surname", 20, TextArea.ANY);
TableLayout.Constraint cn = tl.createConstraint();
cn.setHorizontalSpan(spanButton);
cn.setHorizontalAlign(Component.RIGHT);
hi.add("First Name").add(firstName).
add("Surname").add(surname).
add("E-Mail").add(email).
add("URL").add(url).
add("Phone").add(phone).
add("Credit Card").
add(GridLayout.encloseIn(4, num1, num2, num3, num4)).
add(cn, submit);
197
5.6.1.Masking
A common use case when working with text components is the ability to "mask" input
e.g. in the credit card number above we would want 4 digits for each text field and dont
want the user to tap Next 3 times.
198
});
current.stopEditing();
String val = current.getText();
current.setText(val.substring(0, 4));
next.setText(val.substring(4));
next.startEditing();
199
This will adapt the icon for the action on the keys.
200
However, this behavior might not be desired so to block that we can do:
tf.putClientProperty("iosHideToolbar", Boolean.TRUE);
5.7.Button
28
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionListener.html
https://www.codenameone.com/javadoc/com/codename1/ui/Command.html
201
29
or via a
hi.add(b);
b.addActionListener((e) -> Log.p("Clicked"));
Figure 5.15. Simple button in the iOS styling, notice iOS doesnt
have borders on buttons
Such a button can be styled to look like a link using code like this or simply by making
these settings in the theme and using code such as btn.setUIID("Hyperlink") .
Form hi = new Form("Button");
b.getAllStyles().setBorder(Border.createEmpty());
b.getAllStyles().setTextDecoration(Style.TEXT_DECORATION_UNDERLINE);
hi.add(b);
b.addActionListener((e) -> Log.p("Clicked"));
31
Figure
5.16. Button styled to look like a link
https://www.codenameone.com/javadoc/com/codename1/ui/CheckBox.html
32
https://www.codenameone.com/javadoc/com/codename1/ui/RadioButton.html
202
5.8.CheckBox/RadioButton
CheckBox
31
& RadioButton
32
Notice in the sample below that we associate all the radio buttons with a group but dont
do anything with the group as the radio buttons keep the reference internally. We also
show the opposite side functionality and icon behavior:
CheckBox cb1 = new CheckBox("CheckBox No Icon");
cb1.setSelected(true);
rb2.setSelected(true);
hi.add(cb1).add(cb2).add(cb3).add(cb4).add(rb1).add(rb2).add(rb3);
203
5.8.1.Toggle Button
A toggle button is a button that is pressed and stays pressed. When a toggle button is
pressed again its released from the pressed state. Hence the button has a selected
state to indicate if its pressed or not exactly like the CheckBox / RadioButton
components in Codename One.
204
No Icon");
With Icon", icon);
Opposite True", icon);
Opposite False", icon);
205
33
https://www.codenameone.com/javadoc/com/codename1/ui/ComponentGroup.html
206
207
5.9.ComponentGroup
ComponentGroup
(BoxLayout
35
34
ComponentGroup "restyles" the elements within the group to have a UIID that
allows us to create a "round border" effect that groups elements together.
The following code adds 4 component groups to a Container to demonstrate the
various UIID changes:
hi.add("Three Labels").
add(ComponentGroup.enclose(new Label("GroupElementFirst
add(ComponentGroup.enclose(new Button("ButtonGroupFirst
34
35
https://www.codenameone.com/javadoc/com/codename1/ui/ComponentGroup.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BoxLayout.html
208
209
210
5.10.MultiButton
36
36
37
https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
211
hi.add(oneLineIconEmblem).
add(twoLinesNoIcon).
add(twoLinesIconEmblem).
add(twoLinesIconEmblemHorizontal).
add(twoLinesIconCheckBox).
add(fourLinesIcon);
212
213
5.11.SpanButton
SpanButton
38
Button but can break lines rather than crop them when the text is very long.
Unlike the MultiButton it uses the TextArea internally to break lines seamlessly.
The SpanButton is far simpler than the MultiButton and as a result isnt as
configurable.
SpanButton sb = new SpanButton("SpanButton is a composite component (lead
component) that looks/acts like a Button but can break lines rather than
crop them when the text is very long.");
sb.setIcon(icon);
hi.add(sb);
5.12.SpanLabel
39
SpanLabel
is a composite component (lead component) that looks/acts like a
40
Label but can break lines rather than crop them when the text is very long.
38
39
40
https://www.codenameone.com/javadoc/com/codename1/components/SpanButton.html
https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
214
to position the icon. However, unlike a Label the icon position is determined by the
layout manager of the composite so setIconPosition accepts a BorderLayout
constraint.
SpanLabel d = new SpanLabel("Default SpanLabel that can seamlessly line
break when the text is really long.");
d.setIcon(icon);
215
5.13.OnOffSwitch
41
The OnOffSwitch allows you to write an application where the user can swipe a
switch between two states (on/off). This is a common UI paradigm in Android and iOS,
although its implemented in a radically different way in both platforms.
41
https://www.codenameone.com/javadoc/com/codename1/components/OnOffSwitch.html
216
5.13.1.Validation
42
Validation is an inherent part of text input, and the Validator class allows just that.
You can enable validation thru the Validator class to add constraints for a specific
component. Its also possible to define components that would be enabled/disabled
based on validation state and the way in which validation errors are rendered (change
43
the components UIID , paint an emblem on top, etc.). A Constraint is an interface
that represents validation requirements. You can define a constraint in Java or use
44
45
some of the builtin constraints such as LengthConstraint , RegexConstraint , etc.
This sample below continues from the place where the TextField sample above stopped
by adding validation to that code.
Validator v = new Validator();
addConstraint(url, RegexConstraint.validURL()).
addConstraint(email, RegexConstraint.validEmail()).
v.addSubmitButtons(submit);
42
https://www.codenameone.com/javadoc/com/codename1/ui/validation/Validator.html
43
https://www.codenameone.com/javadoc/com/codename1/ui/validation/Constraint.html
44
https://www.codenameone.com/javadoc/com/codename1/ui/validation/LengthConstraint.html
45
https://www.codenameone.com/javadoc/com/codename1/ui/validation/RegexConstraint.html
218
5.14.InfiniteProgress
46
https://www.codenameone.com/javadoc/com/codename1/components/InfiniteProgress.html
219
InfiniteProgress can also appear over the entire screen, thus blocking all input.
This tints the background while the infinite progress rotates:
Dialog ip = new InfiniteProgress().showInifiniteBlocking();
// do some long operation here using invokeAndBlock or do something in a
separate thread and callback later
https://www.codenameone.com/javadoc/com/codename1/components/
InfiniteProgress.html#setAnimation-com.codename1.ui.Image-
220
48
& InfiniteContainer
49
48
49
https://www.codenameone.com/javadoc/com/codename1/components/InfiniteScrollAdapter.html
https://www.codenameone.com/javadoc/com/codename1/ui/InfiniteContainer.html
221
222
r.addArgument("action", "search_listings");
r.addArgument("encoding", "json");
r.addArgument("listing_type", "buy");
InputStreamReader(new
ByteArrayInputStream(r.getResponseData()), "UTF-8"));
Map<String, Object> response = (Map<String,
Object>)result.get("response");
return (java.util.List<Map<String,
Object>>)response.get("listings");
} catch(Exception err) {
Log.e(err);
return null;
The demo code here doesnt do any error handling! This is a very
bad practice and it is taken here to keep the code short and readable.
Proper error handling is used in the Property Cross demo.
The fetchPropertyData is a very simplistic tool that just fetches the next page of
listings for the nestoria webservice. Notice that this method is synchronous and will
block the calling thread (legally) until the network operation completes.
Now that we have a webservice lets proceed to create the UI. Check out the code
annotations below:
Form hi = new Form("InfiniteScrollAdapter", new
BoxLayout(BoxLayout.Y_AXIS));
Style s = UIManager.getInstance().getComponentStyle("MultiLine1");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder =
EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight()
* 3), false);
InfiniteScrollAdapter.createInfiniteScroll(hi.getContentPane(), () -> {
223
InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), new
Component[0], false);
return;
}
String thumb_url = (String)currentListing.get("thumb_url");
String guid = (String)currentListing.get("guid");
String summary = (String)currentListing.get("summary");
cmps[iter] = new MultiButton(summary);
cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid,
thumb_url));
}
InfiniteScrollAdapter.addMoreComponents(hi.getContentPane(), cmps,
true);
}, true);
50
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
224
5.15.1.The InfiniteContainer
51
}
String thumb_url = (String)currentListing.get("thumb_url");
String guid = (String)currentListing.get("guid");
String summary = (String)currentListing.get("summary");
cmps[iter] = new MultiButton(summary);
cmps[iter].setIcon(URLImage.createToStorage(placeholder, guid,
thumb_url));
51
https://www.codenameone.com/javadoc/com/codename1/ui/InfiniteContainer.html
225
return cmps;
};
hi.add(BorderLayout.CENTER, ic);
5.16.3.MVC In Lists
52
A Codename One List doesnt contain components, but rather arbitrary data; this
seems odd at first but makes sense. If you want a list to contain components, just use
a Container.
The advantage of using a List in this way is that we can display it in many ways
(e.g. fixed focus positions, horizontally, etc.), and that we can have more than a million
entries without performance overhead. We can also do some pretty nifty things, like
filtering the list on the fly or fetching it dynamically from the Internet as the user scrolls
53
down the list. To achieve these things the list uses two interfaces: ListModel and
54
ListCellRenderer. List model represents the data; its responsibility is to return the
arbitrary object within the list at a given offset. Its second responsibility is to notify the
list when the data changes, so the list can refresh.
Think of the model as an array of objects that can notify you when
it changes.
The list renderer is like a rubber stamp that knows how to draw an object from the
model, its called many times per entry in an animated list and must be very fast. Unlike
standard Codename One components, it is only used to draw the entry in the model
and is immediately discarded, hence it has no memory overhead, but if it takes too long
to process a model value it can be a big bottleneck!
Think of the render as a translation layer that takes the "data" from
the model and translates it to a visual representation.
This is all very generic, but a bit too much for most, doing a list "properly" requires some
understanding. The main source of confusion for developers is the stateless nature of
the list and the transfer of state to the model (e.g. a checkbox list needs to listen to
action events on the list and update the model, in order for the renderer to display that
state). Once you understand that its easy.
52
53
54
https://www.codenameone.com/javadoc/com/codename1/ui/List.html
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
https://www.codenameone.com/javadoc/com/codename1/ui/List.html
227
5.16.4.Understanding MVC
Lets recap, what is MVC:
Model - Represents the data for the component (list), the model can tell us exactly
how many items are in it and which item resides at a given offset within the model.
This differs from a simple Vector (or array), since all access to the model is
controlled (the interface is simpler), and unlike a Vector /Array, the model can
notify us of changes that occur within it.
View - The view draws the content of the model. It is a "dumb" layer that has no
notion of what is displayed and only knows how to draw. It tracks changes in the
model (the model sends events) and redraws itself when it changes.
Controller - The controller accepts user input and performs changes to the model,
which in turn cause the view to refresh.
228
55
56
Codename Ones List component uses the MVC paradigm in its implementation.
57
List itself is the Controller (with a bit of the View mixed in). The ListCellRenderer
55
229
58
When the list is painted, it iterates over the visible elements in the model and asks the
model for the data, it then draws them using the renderer. Notice that because of this
both the model and the renderer must be REALLY fast and thats hard.
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListCellRenderer.html
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
230
59
form.setScrollable(false);
form.setLayout(new BorderLayout());
form.add(BorderLayout.CENTER, myList);
60
The MultiList is a preconfigured list that contains a ready made renderer with
defaults that make sense for the most common use cases. It still retains most of the
power available to the List component but reduces the complexity of one of the
hardest things to grasp for most developers: rendering.
The full power of the ListModel is still available and allows you to create a million
entry list with just a few lines of code. However the objects that the model returns should
always be in the form of Map objects and not an arbitrary object like the standard
List allows.
Here is a simple example of a MultiList containing a highly popular subject matter:
Form hi = new Form("MultiList", new BorderLayout());
ArrayList<Map<String, Object>> data = new ArrayList<>();
data.add(createListEntry("A
data.add(createListEntry("A
data.add(createListEntry("A
data.add(createListEntry("A
59
60
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
https://www.codenameone.com/javadoc/com/codename1/ui/list/MultiList.html
231
There is one major piece missing here and that is the cover images for the books. A
simple approach would be to just place the image objects into the entries using the
"icon" property as such:
private Map<String, Object> createListEntry(String name, String date,
Image cover) {
return entry;
233
61
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
234
Lets assume that GRRM was really prolific and wrote 1 million books. The default
list model wont make much sense in that case but we would still be able to render
everything in a list model.
Well fake it a bit but notice that 1M components wont be created even if we somehow
scroll all the way down
63
The ListModel interface can be implemented by anyone in this case we just did a
really stupid simple implementation:
class GRMMModel implements ListModel<Map<String,Object>> {
@Override
index, "1996");
case 1:
index, "1998");
case 2:
index, "2000");
case 3:
index, "2005");
case 4:
index, "2011");
case 5:
index, "Ugh");
}
}
62
63
http://www.georgerrmartin.com/
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
235
return 1000000;
@Override
return 0;
@Override
@Override
@Override
@Override
@Override
@Override
@Override
We can now replace the existing model by removing all the model related logic and
changing the constructor call as such:
MultiList ml = new MultiList(new GRMMModel());
236
Figure 5.31. It took ages to scroll this far This goes to a million
//This method is called by the List for each item, when the List paints
itself.
237
This will compile and work, but wont give you much, notice that you wont see the
64
List selection move on the List, this is just because the renderer returns a Label
with the same style regardless if its selected or not.
Now Lets try to make it a bit more useful.
public Component getListCellRendererComponent(List list, Object value, int
index, boolean isSelected){
if (isSelected) {
l.setFocus(true);
l.getAllStyles().setBgTransparency(100);
} else {
}
}
}
l.setFocus(false);
l.getAllStyles().setBgTransparency(0);
return l;
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
238
setFocus(true);
getAllStyles().setBgTransparency(100);
} else {
setFocus(false);
getAllStyles().setBgTransparency(0);
return this;
}
setLayout(new BorderLayout());
addComponent(BorderLayout.WEST, pic);
name.getAllStyles().setBgTransparency(0);
name.getAllStyles().setFont(Font.createSystemFont(Font.FACE_SYSTEM,
Font.STYLE_BOLD, Font.SIZE_MEDIUM));
65
https://www.codenameone.com/javadoc/com/codename1/ui/list/DefaultListCellRenderer.html
239
focus.getStyle().setBgTransparency(100);
In this renderer we want to render a Contact object to the Screen, we build the
Component in the constructor and in the getListCellRendererComponent we simply
update the Labels' texts according to the Contact object.
Notice that in this renderer we return a focus Label with semi transparency, as
mentioned before, the focus component can be modified within this method.
For example, I can modify the focus Component to have an icon.
focus.getAllStyles().setBgTransparency(100);
try {
focus.setIcon(Image.createImage("/duke.png"));
focus.setAlignment(Component.RIGHT);
ex.printStackTrace();
implementation requirements still manages to make life easier, both in the GUI builder
and outside of it.
66
GenericListCellRenderer
is a renderer designed to be as simple to use as a
Component - Container hierarchy, we effectively crammed most of the common
renderer use cases into one class. To enable that, we need to know the content of the
objects within the model, so the GenericListCellRenderer assumes the model
contains only Map objects. Since Maps can contain arbitrary data the list model is
still quite generic and allows storing application specific data. Furthermore a Map can
still be derived and extended to provide domain specific business logic.
The GenericListCellRenderer accepts two container instances (more later on
why at least two, and not one), which it maps to individual Map entries within the
model, by finding the appropriate components within the given container hierarchy.
Components are mapped to the Map entries based on the name property of the
component ( getName / setName ) and the key/value within the Map , e.g.:
For a model that contains a Map entry like this:
"Foo": "Bar"
"X": "Y"
"Not": "Applicable"
"Number": Integer(1)
A renderer will loop over the component hierarchy in the container, searching for
components whose name matches Foo, X, Not, and Number, and assigning the
appropriate value to them.
You can also use image objects as values, and they will be assigned
to labels as expected. However, you cant assign both an image and
a text to a single label, since the key will be taken. That isnt a big
problem, since two labels can be used quite easily in such a renderer.
To make matters even more attractive the renderer seamlessly supports list tickering
67
when appropriate, and if a CheckBox appears within the renderer, it will toggle a
boolean flag within the Map seamlessly.
66
67
https://www.codenameone.com/javadoc/com/codename1/ui/list/GenericListCellRenderer.html
https://www.codenameone.com/javadoc/com/codename1/ui/CheckBox.html
241
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
Fisheye is an effect where the selection stays in place as the list moves around it
242
com.codename1.ui.List(createGenericListCellRendererModelData());
list.setRenderer(new
GenericListCellRenderer(createGenericRendererContainer(),
createGenericRendererContainer()));
selected.setName("Selected");
selected.setFocusable(true);
Container c = BorderLayout.center(name).
add(BorderLayout.SOUTH, surname).
add(BorderLayout.WEST, selected);
c.setUIID("ListRenderer");
}
return c;
data[0].put("Name", "Shai");
data[0].put("Surname", "Almog");
data[0].put("Selected", Boolean.TRUE);
data[1] = new HashMap<>();
data[1].put("Name", "Chen");
data[1].put("Surname", "Fishbein");
data[1].put("Selected", Boolean.TRUE);
data[2] = new HashMap<>();
data[2].put("Name", "Ofir");
data[2].put("Surname", "Leitner");
data[3] = new HashMap<>();
data[3].put("Name", "Yaniv");
data[3].put("Surname", "Vakarat");
data[4] = new HashMap<>();
data[4].put("Name", "Meirav");
data[4].put("Surname", "Nachmanovitch");
return data;
243
https://www.codenameone.com/javadoc/com/codename1/ui/list/MultiList.html
244
Normally, to build the model for a renderer of this type, we use something like:
map.put("componentName", "Component Value");
This will apply the UIID "red" to the component, which you can then style in the theme.
Notice that once you start doing this, you need to define this entry for all entries, e.g.:
map.put("componentName_uiid", "blue");
Otherwise the component will stay red for the next entry (since the renderer acts like
a rubber stamp).
Rendering Prototype
Because of the rendering architecture of a List its pretty hard to calculate the right
preferred size for such a component. The default behavior includes querying a few
entries from the model then constructing their renderers to get a "sample" of the
preferred size value.
As you might guess this triggers a performance penalty that is paid with every reflow
of the UI. The solution is to use setRenderingPrototype .
setRenderingPrototype accepts a "fake" value that represents a reasonably
large amount of data and it will be used to calculate the preferred size. E.g. for a multiList
that should render 2 lines of text with 20 characters and a 5mm square icon I can do
something like this:
Map<String, Object> proto = new HashMap<>();
map.put("Line1", "WWWWWWWWWWWWWWWWWWWW");
map.put("Line2", "WWWWWWWWWWWWWWWWWWWW");
245
5.16.9.ComboBox
71
https://www.codenameone.com/javadoc/com/codename1/ui/ComboBox.html
https://www.codenameone.com/javadoc/com/codename1/ui/spinner/Picker.html
https://www.codenameone.com/javadoc/com/codename1/ui/AutoCompleteTextField.html
246
5.17.Slider
74
https://www.codenameone.com/javadoc/com/codename1/ui/Slider.html
247
The slider itself is initialized in the code below. Notice that you can achieve almost the
same result using a theme by setting the Slider & SliderFull UIIDs (both in
selected & unselected states).
In fact doing this in the theme might be superior as you could use one image that
contains 5 stars already and that way you wont need the preferred size hack below:
private void initStarRankStyle(Style s, Image star) {
s.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH);
s.setBorder(Border.createEmpty());
s.setBgImage(star);
s.setBgTransparency(0);
starRank.setEditable(true);
starRank.setMinValue(0);
starRank.setMaxValue(10);
Font fnt =
Font.createTrueTypeFont("native:MainLight", "native:MainLight").
derive(Display.getInstance().convertToPixels(5, true),
Font.STYLE_PLAIN);
Style s = new Style(0xffff33, 0, fnt, (byte)0);
248
s.setFgColor(0);
Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR,
s).toImage();
initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar);
initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(),
emptyStar);
initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar);
initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar);
starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5,
fullStar.getHeight()));
}
return starRank;
hi.add(FlowLayout.encloseCenter(createStarRankSlider()));
hi.show();
5.18.Table
75
https://www.codenameone.com/javadoc/com/codename1/ui/table/Table.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
https://www.codenameone.com/javadoc/com/codename1/ui/table/TableLayout.html
249
1",
2",
3",
4",
"Row
"Row
"Row
"Row
A",
B",
C",
D",
"Row
"Row
"Row
"Row
X"},
Y"},
Z"},
K"},
};
return col != 0;
hi.add(BorderLayout.CENTER, table);
hi.show();
250
251
};
return col != 0;
TableLayout.Constraint con =
row, column);
super.createCellConstraint(value,
}
con.setWidthPercentage(33);
}
return con;
};
hi.add(BorderLayout.CENTER, table);
252
253
p.setType(Display.PICKER_TYPE_STRINGS);
p.setStrings("Row B can now stretch", "This is a good
value", "So Is This", "Better than text field");
p.setSelectedString((String)value);
p.setUIID("TableCell");
p.addActionListener((e) -> getModel().setValueAt(row, column,
p.getSelectedString()));
cell = p;
} else {
}
}
}
cell.getAllStyles().setBgColor(0xeeeeee);
cell.getAllStyles().setBgTransparency(255);
return cell;
@Override
TableLayout.Constraint con =
row, column);
super.createCellConstraint(value,
}
con.setWidthPercentage(33);
};
return con;
254
Figure 5.38. Table with customize cells using the pinstripe effect
255
78
79
https://www.codenameone.com/javadoc/com/codename1/ui/TextArea.html
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
256
{"Row 2", "Row B can now stretch very long line that should span
multiple rows as much as possible", "Row Y"},
{"Row 3", "Row C", "Row Z"},
{"Row 4", "Row D", "Row K"},
}) {
};
return col != 0;
return ta;
@Override
TableLayout.Constraint con =
row, column);
con.setWidthPercentage(33);
}
super.createCellConstraint(value,
return con;
};
hi.add(BorderLayout.CENTER, table);
hi.show();
257
258
Figure 5.41. Multiline table cell in landscape mode. Notice the cell
row count adapts seamlessly
5.19.Tree
80
Tree allows displaying hierarchical data such as folders and files in a collapsible/
expandable UI. Like the Table it is a composite component (but it isnt a lead
component). Like the Table it works in consort with a model to construct its user
interface on the fly but doesnt use a stateless renderer (as List does).
The data of the Tree arrives from a model model e.g. this:
class StringArrayTreeModel implements TreeModel {
String[][] arr = new String[][] {
};
https://www.codenameone.com/javadoc/com/codename1/ui/tree/Tree.html
259
v.addElement(arr[0][iter]);
return v;
}
}
v.addElement(arr[iter + 1][i]);
return v;
260
return ((Element)child).getTagName();
return child.toString();
};
hi.add(BorderLayout.CENTER, xmlTree);
} catch(IOException err) {
Log.e(err);
261
public XMLTreeModel(Element e) {
262
root = e;
return c;
result.addElement(e.getChildAt(iter));
return result;
return e.getNumChildren() == 0;
5.20.ShareButton
ShareButton
of text.
81
is a button you can add into the UI to let a user share an image or block
https://www.codenameone.com/javadoc/com/codename1/components/ShareButton.html
https://www.codenameone.com/javadoc/com/codename1/io/FileSystemStorage.html
https://www.codenameone.com/javadoc/com/codename1/io/FileSystemStorage.html
263
83
FileSystemStorage.getInstance().openOutputStream(imageFile)) {
ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1);
} catch(IOException err) {
Log.e(err);
}
sb.setImageToShare(imageFile, "image/png");
264
265
Figure 5.45. The share button running on the Android device and
screenshot sent into twitter
The ShareButton features some share service classes to allow
plugging in additional share services. However, this functionality is
only relevant to devices where native sharing isnt supported. So this
code isnt used on iOS/Android
5.21.Tabs
84
The Tabs
Container arranges components into groups within "tabbed"
containers. Tabs is a container type that allows leafing through its children using
labeled toggle buttons. The tabs can be placed in multiple different ways (top, bottom,
left or right) with the default being determined by the platform. This class also allows
84
https://www.codenameone.com/javadoc/com/codename1/ui/Tabs.html
266
the add method. That method wont work since a Tab can have both an Image and
text String associated with it.
Form hi = new Form("Tabs", new BorderLayout());
Tabs t = new Tabs();
Style s = UIManager.getInstance().getComponentStyle("Tab");
FontImage icon1 =
FontImage.createMaterial(FontImage.MATERIAL_QUESTION_ANSWER, s);
Container container1 = BoxLayout.encloseY(new Label("Label1"), new
Label("Label2"));
t.addTab("Tab1", icon1, container1);
267
https://www.codenameone.com/javadoc/com/codename1/ui/RadioButton.html
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/LayeredLayout.html
268
Style s = UIManager.getInstance().getComponentStyle("Button");
FontImage radioEmptyImage =
FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_UNCHECKED, s);
FontImage radioFullImage =
FontImage.createMaterial(FontImage.MATERIAL_RADIO_BUTTON_CHECKED, s);
((DefaultLookAndFeel)UIManager.getInstance().getLookAndFeel()).setRadioButtonImages(radi
radioEmptyImage, radioFullImage, radioEmptyImage);
Container container1 = BoxLayout.encloseY(new Label("Swipe the tab to see
more"),
t.addTab("Tab1", container1);
firstTab.setSelected(true);
Container tabsFlow = FlowLayout.encloseCenter(firstTab, secondTab);
hi.add(t);
hi.add(BorderLayout.south(tabsFlow));
t.addSelectionListener((i1, i2) -> {
switch(i2) {
case 0:
if(!firstTab.isSelected()) {
}
firstTab.setSelected(true);
break;
case 1:
if(!secondTab.isSelected()) {
}
});
secondTab.setSelected(true);
break;
269
270
271
The MediaPlayer allows you to control video playback. To use the MediaPlayer
88
we need to first load the Media object from the MediaManager .
The MediaManager is the core class responsible for media interaction in Codename
One.
89
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon =
FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s);
hi.getToolbar().addCommandToRightBar("", icon, (evt) -> {
Display.getInstance().openGallery((e) -> {
if(e != null && e.getSource() != null) {
String file = (String)e.getSource();
try {
} catch(IOException err) {
Log.e(err);
}
}
}, Display.GALLERY_VIDEO);
});
hi.show();
87
88
89
https://www.codenameone.com/javadoc/com/codename1/components/MediaPlayer.html
https://www.codenameone.com/javadoc/com/codename1/media/MediaManager.html
https://www.codenameone.com/javadoc/com/codename1/capture/Capture.html
272
273
274
5.23.ImageViewer
90
The ImageViewer allows us to inspect, zoom and pan into an image. It also allows
swiping between images if you have a set of images (using an image list model).
The ImageViewer is a complex rich component designed for user
91
interaction. If you just want to display an image use Label if you
92
want the image to scale seamlessly use ScaleImageLabel .
You can use the ImageViewer as a tool to view a single image which allows you to
zoom in/out to that image as such:
Form hi = new Form("ImageViewer", new BorderLayout());
ImageViewer iv = new ImageViewer(duke);
hi.add(BorderLayout.CENTER, iv);
90
91
92
https://www.codenameone.com/javadoc/com/codename1/components/ImageViewer.html
https://www.codenameone.com/javadoc/com/codename1/ui/Label.html
https://www.codenameone.com/javadoc/com/codename1/components/ScaleImageLabel.html
275
Figure 5.51. ImageViewer as the demo loads with the image from
the default icon
276
277
93
93
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
278
"http://awoiaf.westeros.org/images/thumb/9/93/
AGameOfThrones.jpg/300px-AGameOfThrones.jpg",
"http://awoiaf.westeros.org/images/thumb/3/39/
AClashOfKings.jpg/300px-AClashOfKings.jpg",
"http://awoiaf.westeros.org/images/thumb/2/24/
AStormOfSwords.jpg/300px-AStormOfSwords.jpg",
"http://awoiaf.westeros.org/images/thumb/a/a3/
AFeastForCrows.jpg/300px-AFeastForCrows.jpg",
"http://awoiaf.westeros.org/images/7/79/ADanceWithDragons.jpg"
};
private Image[] images;
images[index] = placeholder;
Util.downloadUrlToStorageInBackground(imageURLs[index], "list"
+ index, (e) -> {
try {
images[index] =
EncodedImage.create(Storage.getInstance().createInputStream("list" +
index));
listeners.fireDataChangeEvent(index,
DataChangedListener.CHANGED);
} catch(IOException err) {
err.printStackTrace();
279
}
}
});
return images[index];
return imageURLs.length;
return selection;
selection = index;
listeners.addListener(l);
listeners.removeListener(l);
280
94
This fetches the images in the URL asynchronously and fires a data change event when
the data arrives to automatically refresh the ImageViewer when that happens.
94
281
95
& ScaleImageButton
96
shrink to fit available space. In that sense they differ from Label & Button which keeps
the image at the same size.
The default UIID of ScaleImageLabel is Label, however the
default UIID of ScaleImageButton is ScaleImageButton. The
reasoning for the difference is that the Button UIID includes a
border and a lot of legacy.
You can use ScaleImageLabel / ScaleImageButton interchangeably. The only
major difference between these components is the buttons ability to handle click events/
focus.
Here is a simple example that also shows the difference between the scale to fill
and scale to fit modes:
TableLayout tl = new TableLayout(2, 2);
Style s = UIManager.getInstance().getComponentStyle("Button");
Image icon = FontImage.createMaterial(FontImage.MATERIAL_WARNING, s);
ScaleImageLabel fillLabel = new ScaleImageLabel(icon);
fillLabel.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
ScaleImageButton fillButton = new ScaleImageButton(icon);
fillButton.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
hi.add(tl.createConstraint().widthPercentage(20), new
ScaleImageButton(icon)).
add(tl.createConstraint().widthPercentage(80), new
ScaleImageLabel(icon)).
add(fillLabel).
add(fillButton);
hi.show();
95
96
https://www.codenameone.com/javadoc/com/codename1/components/ScaleImageLabel.html
https://www.codenameone.com/javadoc/com/codename1/components/ScaleImageButton.html
282
5.25.Toolbar
97
The Toolbar API provides deep customization of the title bar area with more flexibility
98
e.g. placing a TextField for search or buttons in arbitrary title area positions. The
Toolbar API replicates some of the native functionality available on Android/iOS and
integrates with features such as the side menu to provide very fine grained control over
the title area behavior.
The Toolbar needs to be installed into the Form in order for it to work. You can
setup the Toolbar in one of these three ways:
1. form.setToolbar(new Toolbar()); - allows you to activate the Toolbar
to a specific Form and not for the entire application
2. Toolbar.setGlobalToolbar(true); - enables the Toolbar for all the
forms in the app
3. Theme
constant
globalToobarBool
Toolbar.setGlobalToolbar(true);
this
is
equivalent
to
The basic functionality of the Toolbar includes the ability to add a command to the
following 4 places:
97
98
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
283
Log.p("Clicked"));
hi.getToolbar().addCommandToOverflowMenu("Overflow", icon, (e) ->
Log.p("Clicked"));
hi.getToolbar().addCommandToSideMenu("Sidemenu", icon, (e) ->
Log.p("Clicked"));
hi.show();
Toolbar.setGlobalToolbar(true);
Style s = UIManager.getInstance().getComponentStyle("Title");
Form hi = new Form("Toolbar", new BoxLayout(BoxLayout.Y_AXIS));
TextField searchField = new TextField("", "Toolbar Search");
searchField.getHintLabel().setUIID("Title");
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(Component.LEFT);
hi.getToolbar().setTitleComponent(searchField);
FontImage searchIcon = FontImage.createMaterial(FontImage.MATERIAL_SEARCH,
s);
searchField.addDataChangeListener((i1, i2) -> {
String t = searchField.getText();
if(t.length() < 1) {
cmp.setHidden(false);
cmp.setVisible(true);
} else {
t = t.toLowerCase();
val = ((Label)cmp).getText();
} else {
val = ((TextArea)cmp).getText();
} else {
val = (String)cmp.getPropertyValue("text");
-1;
cmp.setHidden(!show);
cmp.setVisible(show);
}
hi.getContentPane().animateLayout(250);
});
hi.getToolbar().addCommandToRightBar("", searchIcon, (e) -> {
});
searchField.startEditingAsync();
286
We use a TextField the whole time and just style it to make it (and its hint) look
like a regular title. An alternative way is to replace the title component dynamically.
We use the DataChangeListener to update the search results as we type
them.
Hidden & Visible use the opposite flag values to say similar things (e.g. when
hidden is set to false you would want to set visible to true).
Visible indicates whether a component can be seen. It will still occupy the physical
space on the screen even when it isnt visible. Hidden will remove the space
occupied by the component from the screen, but some code might still try to paint
it. Normally, visible is redundant but we use it with hidden for good measure.
The search button is totally unnecessary here. We can just click the TextField !
However, that isnt intuitive to most users so we added the button to start editing.
287
288
5.25.1.Search Mode
While you can implement search manually using the builtin search offers a simpler and
more uniform UI.
289
duke = Image.createImage("/duke.png");
} catch(IOException err) {
}
Log.e(err);
Display.getInstance().scheduleBackgroundTask(()-> {
// this will take a while...
m.setTextLine1(c.getDisplayName());
m.setTextLine2(c.getPrimaryPhoneNumber());
Image pic = c.getPhoto();
290
m.setIcon(fill(pic, finalDuke.getWidth(),
finalDuke.getHeight()));
} else {
m.setIcon(finalDuke);
}
hi.add(m);
});
});
}
hi.revalidate();
hi.getToolbar().addSearchCommand(e -> {
String text = (String)e.getSource();
}
hi.getContentPane().animateLayout(150);
} else {
text = text.toLowerCase();
line1.toLowerCase().indexOf(text) > -1 ||
line2 != null && line2.toLowerCase().indexOf(text) >
-1;
mb.setHidden(!show);
mb.setVisible(show);
}
}
}, 4);
hi.getContentPane().animateLayout(150);
hi.show();
5.25.2.Title Animations
Modern UIs often animate the title upon scrolling to balance the highly functional
smaller title advantage with the gorgeous large image based title. This is pretty easy to
do with the Toolbar API thru the Title animation API.
291
EncodedImage placeholder =
EncodedImage.createFromImage(Image.createImage(hi.getWidth(),
hi.getWidth() / 5, 0xffff0000), true);
URLImage background = URLImage.createToStorage(placeholder, "400pxAGameOfThrones.jpg",
"http://awoiaf.westeros.org/images/thumb/9/93/
AGameOfThrones.jpg/400px-AGameOfThrones.jpg");
background.fetch();
Style stitle = hi.getToolbar().getTitleComponent().getUnselectedStyle();
stitle.setBgImage(background);
stitle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stitle.setPaddingUnit(Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS,
Style.UNIT_TYPE_DIPS, Style.UNIT_TYPE_DIPS);
stitle.setPaddingTop(15);
292
Lannister and House Martell both stalled due to insults against their
houses by the Targaryens. The civil war climaxed with the Battle of the
Trident, when Prince Rhaegar was killed in battle by Robert Baratheon.
The Lannisters finally agreed to support King Aerys, but then brutally...
")).
add(credit);
ComponentAnimation title =
hi.getToolbar().getTitleComponent().createStyleAnimation("Title", 200);
hi.getAnimationManager().onTitleScrollAnimation(title);
hi.show();
293
Figure 5.62. The Toolbar starts with the large URLImage fetched
from the web
294
Figure 5.63. As we scroll down the image fades and the title shrinks
in size returning to the default UIID look
295
In the first line we create a style animation that will translate the style from the current
settings to the destination UIID (the first argument) within 200 pixels of scrolling. We
then bind this animation to the title scrolling animation event.
296
The WebBrowser
component shows the native device web browser when supported
101
by the device and the HTMLComponent
when the web browser isnt supported
on the given device. If you only intend to target smartphones you should use the
102
BrowserComponent
directly instead of the WebBrowser .
The BrowserComponent can point at an arbitrary URL to load it:
Form hi = new Form("Browser", new BorderLayout());
BrowserComponent browser = new BrowserComponent();
browser.setURL("https://www.codenameone.com/");
hi.add(BorderLayout.CENTER, browser);
100
101
102
https://www.codenameone.com/javadoc/com/codename1/components/WebBrowser.html
https://www.codenameone.com/javadoc/com/codename1/ui/html/HTMLComponent.html
https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html
297
298
5.26.1.BrowserComponent Hierarchy
When Codename One packages applications into native apps it hides a lot of details
to make the process simpler. One of the things hidden is the fact that we arent dealing
with a JAR anymore, so getResource / getResourceAsStream are problematic
Both of these APIs support hierarchies and a concept of package relativity both of
which might not be supported on all OSs.
103
Thats why we recommend that you place files inside res files. A
resource file allows you to add arbitrary data files and you can have
as many resource files as you need.
For web developers this isnt enough since hierarchies are used often to represent the
various dependencies, this means that many links & references are relative. To work
with such hierarchies just place all of your resources in a hierarchy under the html
package in the project root. The build server will tar the entire content of that package
and add an html.tar file into the native package. This tar is seamlessly extracted
on the device when you actually need the resources and only with new application
versions (not on every launch). So assuming the resources are under the html root
package they can be displayed with code like this:
103
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
299
try {
browserComponent.setURLHierarchy("/htmlFile.html");
} catch(IOException err) {
}
...
Notice that the path is relative to the html directory and starts with / but inside the
HTML files you should use relative (not absolute) paths.
Also notice that an IOException can be thrown due to the process of untarring. Its
unlikely to happen but is entirely possible.
5.26.2.NavigationCallback
104
https://www.codenameone.com/javadoc/com/codename1/ui/events/BrowserNavigationCallback.html
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html#callSerially-
java.lang.Runnable-
300
<meta charset=\"utf-8\">\n" +
"
"
"
<script>\n" +
function fnc(message) {\n" +
document.write(message);\n" +
"
};\n" +
"
</script>\n" +
"
</head>\n" +
"
<body >\n" +
"
<p><a href=\"http://click\">Demo</a></p>\n" +
"
</body>\n" +
"</html>", null);
hi.add(BorderLayout.CENTER, bc);
bc.setBrowserNavigationCallback((url) -> {
if(url.startsWith("http://click")) {
return false;
return true;
Figure 5.66. Before the link is clicked for the "shouldNavigate" call
Figure 5.67. After the link is clicked for the "shouldNavigate" call
The JavaScript Bridge is implemented
BrowserNavigationCallback .
301
on
top
of
the
5.26.3.JavaScript
The JavaScript bridge is sometimes confused with the JavaScript
Port. The JavaScript bridge allows us to communicate with
JavaScript from Java (and visa versa). The JavaScript port allows
you to compile the Codename One application into a JavaScript
application that runs in a standard web browser without code
changes (think GWT without source changes and with thread
support).+ We discuss the JavaScript port further later in the guide.
BrowserComponent can communicate with the HTML code using JavaScript calls.
E.g. we can create HTML like this:
Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
hi.add(BorderLayout.CENTER, bc).
add(BorderLayout.SOUTH, tf);
bc.addWebEventListener("onLoad", (e) -> bc.execute("fnc('<p>Hello World</
p>')"));
tf.addActionListener((e) -> bc.execute("fnc('<p>" + tf.getText() +"</
p>')"));
hi.show();
302
Figure 5.68. JavaScript code was invoked to append text into the
browser image above
Notice that opening an alert in an embedded native browser might
not work
We use the execute method above to execute custom JavaScript code. We also
have an executeAndReturnString method that allows us to receive a response
value from the JavaScript side.
Coupled with shouldNavigate we can effectively do everything which is exactly
what the JavaScript Bridge tries to do.
The JavascriptContext
class lays the foundation by enabling you to call JavaScript
code directly from Java. It provides automatic type conversion between Java and
JavaScript types as follows:
106
https://www.codenameone.com/javadoc/com/codename1/javascript/JavascriptContext.html
303
Javascript Type
String
String
Double/Integer/Float/Long
Number
Boolean
Boolean
JSObject
Object
null
null
Other
Not Allowed
Java Type
String
String
Number
Double
Boolean
Boolean
Object
JSObject
Function
JSObject
Array
JSObject
null
null
undefined
null
This conversion table is more verbose than necessary, since
JavaScript functions and arrays are, in fact Objects themselves, so
those rows are redundant. All JavaScript objects are converted to
JSObject
107
We can access JavaScript variables easily from the context by using code like this:
Form hi = new Form("BrowserComponent", new BorderLayout());
BrowserComponent bc = new BrowserComponent();
https://www.codenameone.com/javadoc/com/codename1/javascript/JSObject.html
304
<body >\n" +
"
<p>This will appear twice...</p>\n" +
"
</body>\n" +
"</html>", null);
hi.add(BorderLayout.CENTER, bc);
bc.addWebEventListener("onLoad", (e) -> {
});
hi.show();
Figure 5.69. The contents was copied from the DOM and placed in
the south position of the form
Notice that when you work with numeric values or anything related to the types
mentioned above your code must be aware of the typing. E.g. in this case the type is
Double and not String :
Double outerWidth = (Double)ctx.get("window.outerWidth");
You can also query the context for objects and modify their value e.g.
Form hi = new Form("BrowserComponent", new BorderLayout());
305
});
JSObject jo = (JSObject)ctx.get("window");
jo.set("location", "https://www.codenameone.com/");
This code effectively navigates to the Codename One home page by fetching
the DOMs window object and setting its location property to https://
www.codenameone.com/.
5.26.4.Cordova/PhoneGap Integration
PhoneGap was one of the first web app packager tools in the market. Its a tool that
is effectively a browser component within a native wrapper coupled with native access
APIs. Cordova is the open source extension of this popular project.
Codename One supports embedding PhoneGap/Cordova applications directly
into Codename One applications. This is relatively easy to do with the
BrowserComponent and JavaScript integration. The main aspect that this integration
requires is support for Cordova plugins & its JavaScript APIs.
https://github.com/codenameone/CN1Cordova
306
109
5.27.AutoCompleteTextField
110
The AutoCompleteTextField
allows us to write text into a text field and select a
completion entry from the list in a similar way to a search engine.
111
This is really easy to incorporate into your code, just replace your usage of TextField
with AutoCompleteTextField and define the data that the autocomplete should
work from. There is a default implementation that accepts a String array or a
112
ListModel
for completion strings, this can work well for a "small" set of thousands
(or tens of thousands) of entries.
E.g. This is a trivial use case that can work well for smaller sample sizes:
Form hi = new Form("Auto Complete", new BoxLayout(BoxLayout.Y_AXIS));
AutoCompleteTextField ac = new
109
https://www.codenameone.com/blog/phonegap-cordova-compatibility-for-codename-one.html
110
https://www.codenameone.com/javadoc/com/codename1/ui/AutoCompleteTextField.html
111
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
112
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
307
return false;
String[] l = searchLocations(text);
if(l == null || l.length == 0) {
}
return false;
options.removeAll();
for(String s : l) {
}
}
options.addItem(s);
return true;
308
ac.setMinimumElementsShownInPopup(5);
hi.add(ac);
if(text.length() > 0) {
r.setPost(false);
r.setUrl("https://maps.googleapis.com/maps/api/place/
autocomplete/json");
r.addArgument("key", apiKey.getText());
r.addArgument("input", text);
NetworkManager.getInstance().addToQueueAndWait(r);
Map<String,Object> result = new
JSONParser().parseJSON(new InputStreamReader(new
ByteArrayInputStream(r.getResponseData()), "UTF-8"));
String[] res = Result.fromContent(result).getAsStringArray("//
description");
}
return res;
} catch(Exception err) {
}
}
Log.e(err);
return null;
309
5.28.Picker
113
Picker
occupies the limbo between native widget and lightweight widget. Picker is
more like TextField / TextArea in the sense that its a Codename One widget that
calls the native code only during editing.
The reasoning for this is the highly native UX and functionality related to this widget
type which should be quite obvious from the screenshots below.
At this time there are 4 types of pickers:
Time
Date & Time
Date
Strings
If a platform doesnt support native pickers an internal fallback implementation is used.
This is the implementation we always use in the simulator so assume different behavior
when building for the device.
While Android supports Date, Time native pickers it doesnt support
the Date & Time native picker UX and will fallback in that case.
113
https://www.codenameone.com/javadoc/com/codename1/ui/spinner/Picker.html
310
datePicker.setType(Display.PICKER_TYPE_DATE);
Picker dateTimePicker = new Picker();
dateTimePicker.setType(Display.PICKER_TYPE_DATE_AND_TIME);
Picker timePicker = new Picker();
timePicker.setType(Display.PICKER_TYPE_TIME);
Picker stringPicker = new Picker();
stringPicker.setType(Display.PICKER_TYPE_STRINGS);
datePicker.setDate(new Date());
dateTimePicker.setDate(new Date());
311
312
313
Figure 5.75. Date & time picker on Android. Notice it didnt use a
builtin widget since there is none
314
315
316
5.29.SwipeableContainer
The SwipeableContainer
114
115
on top of additional "options" that can be exposed by swiping the component to the side.
This swipe gesture is commonly used in touch interfaces to expose features such as
delete, edit etc. Its trivial to use this component by just determining the components
placed on top and bottom (the revealed component).
SwipeableContainer swip = new SwipeableContainer(bottom, top);
We can combine some of the demos above including the Slider stars demo to rank
GRRMs books in an interactive way:
Form hi = new Form("Swipe", new BoxLayout(BoxLayout.Y_AXIS));
hi.add(createRankWidget("A Game of Thrones", "1996")).
add(createRankWidget("A Clash Of Kings", "1998")).
add(createRankWidget("A Storm Of Swords", "2000")).
add(createRankWidget("A Feast For Crows", "2005")).
add(createRankWidget("A Dance With Dragons", "2011")).
add(createRankWidget("The Winds of Winter", "TBD")).
add(createRankWidget("A Dream of Spring", "TBD"));
hi.show();
SwipeableContainer(FlowLayout.encloseCenterMiddle(createStarRankSlider()),
button);
114
115
https://www.codenameone.com/javadoc/com/codename1/ui/SwipeableContainer.html
https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html
317
5.30.EmbeddedContainer
116
EmbeddedContainer
solves a problem that exists only within the GUI builder and
the class makes no sense outside of the context of the GUI builder. The necessity
for EmbeddedContainer came about due to iPhone inspired designs that relied on
116
https://www.codenameone.com/javadoc/com/codename1/ui/util/EmbeddedContainer.html
318
5.31.MapComponent
The MapComponent uses a somewhat outdated tiling API which
is not as rich as modern native maps. We recommend using
118
the GoogleMaps Native cn1lib
to integrate native mapping
functionality into the Codename One app.
The MapComponent
navigatable map.
117
118
119
119
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
https://github.com/codenameone/codenameone-google-maps/
https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html
319
map.setLayout(new BorderLayout());
map.setScrollable(false);
Location loc =
LocationManager.getLocationManager().getCurrentLocation();
Coord lastLocation = new Coord(loc.getLatitude(), loc.getLongtitude());
Image i = Image.createImage("/blue_pin.png");
PointsLayer pl = new PointsLayer();
pl.setPointIcon(i);
320
mc.addLayer(pl);
}
mc.zoomToLayers();
map.addComponent(BorderLayout.CENTER, mc);
map.addCommand(new BackCommand());
map.setBackCommand(new BackCommand());
map.show();
120
https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html
https://www.codenameone.com/javadoc/com/codename1/location/Location.html
https://www.codenameone.com/javadoc/com/codename1/location/Location.html
321
LocationManager.getLocationManager().getCurrentLocation();
//use the code from above to show you on the map
putMeOnMap(mc);
map.addComponent(BorderLayout.CENTER, mc);
map.addCommand(new BackCommand());
map.setBackCommand(new BackCommand());
ConnectionRequest req = new ConnectionRequest() {
IOException {
"
+ "https://developers.google.com/maps/
documentation/places/");
progress.dispose();
Dialog.show("Info", "make sure to obtain an
application key from "
+ "google places api's"
, "Ok", null);
}
return;
pl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
null);
322
Coord(lat.doubleValue(), lng.doubleValue()),
(String) entry.get("name"), null);
pl.addPoint(point);
}
progress.dispose();
mc.addLayer(pl);
map.show();
mc.zoomToLayers();
}
};
req.setUrl("https://maps.googleapis.com/maps/api/place/search/
json");
req.setPost(false);
req.addArgument("location", "" + loc.getLatitude() + "," +
loc.getLongtitude());
req.addArgument("radius", "500");
req.addArgument("types", "food");
req.addArgument("sensor", "false");
//get your own key from https://developers.google.com/maps/
documentation/places/
NetworkManager.getInstance().addToQueue(req);
ex.printStackTrace();
323
5.32.Chart Component
The charts package enables Codename One developers to add charts and
5.32.1.Device Support
Since the charts package makes use of 2D transformations and shapes, it requires
some of the graphics features that are not yet available on all platforms. Currently the
following platforms are supported:
1. Simulator
2. Android
3. iOS
5.32.2.Features
1. Built-in support for many common types of charts including bar charts, line
charts, stacked charts, scatter charts, pie charts and more.
2. Pinch Zoom - The ChartComponent
support.
123
5.32.3.Chart Types
The com.codename1.charts package includes models and renderers for many
different types of charts. It is also extensible so that you can add your own chart types
if required. The following screen shots demonstrate a small sampling of the types of
charts that can be created.
123
124
https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html
https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html
324
325
326
327
328
329
125
https://github.com/codenameone/codenameone-demos/tree/master/ChartsDemo
330
4. Create a ChartComponent . In order to add your chart to the UI, you need to
127
wrap it in a ChartComponent
object.
128
}
}
r.setColor(color);
renderer.addSeriesRenderer(r);
return renderer;
/**
* Builds a category series using the provided values.
*
126
127
128
https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html
https://www.codenameone.com/javadoc/com/codename1/charts/ChartComponent.html
https://github.com/codenameone/codenameone-demos/tree/master/ChartsDemo
331
return series;
object.
values), renderer);
f.setLayout(new BorderLayout());
332
5.33.Calendar
129
The Calendar
class allows us to display a traditional calendar picker and optionally
highlight days in various ways.
We normally recommend developers use the Picker UI rather than
use the calendar to pick a date. It looks better on the devices.
Simple usage of the Calendar class looks something like this:
Form hi = new Form("Calendar", new BorderLayout());
Calendar cld = new Calendar();
129
https://www.codenameone.com/javadoc/com/codename1/ui/Calendar.html
333
5.34.ToastBar
130
The ToastBar
class allows us to display none-obtrusive status messages to the user
at the bottom of the screen. This is useful for such things as informing the user of a
long-running task (like downloading a file in the background), or popping up an error
message that doesnt require a response from the user.
Simple usage of the ToastBar class looks something like this:
130
https://www.codenameone.com/javadoc/com/codename1/components/ToastBar.html
334
status.clear();
335
336
status.show();
We can also delay the showing of the status message using showDelayed as such:
Status status = ToastBar.getInstance().createStatus();
status.setMessage("Hello world");
status.clear();
337
5.35.SignatureComponent
The SignatureComponent
signature in the app.
131
https://www.codenameone.com/javadoc/com/codename1/components/SignatureComponent.html
338
hi.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
hi.add("Enter Your Name:");
hi.add(new TextField());
hi.add("Signature:");
sig.addActionListener((evt)-> {
System.out.println("The signature was changed");
Image img = sig.getSignatureImage();
});
hi.addComponent(sig);
hi.show();
339
340
5.36.Accordion
The Accordion
132
f.add(accr);
f.show();
132
https://www.codenameone.com/javadoc/com/codename1/components/Accordion.html
341
342
5.37.Floating Hint
133
FloatingHint
wraps a text component with a special container that can animate the
hint label into a title label when the text component is edited or has content within it.
Form hi = new Form("Floating Hint", BoxLayout.y());
TextField first = new TextField("", "First Field");
add(new FloatingHint(second)).
add(new Button("Go"));
hi.show();
133
https://www.codenameone.com/javadoc/com/codename1/components/FloatingHint.html
343
6.1.Layout Reflow
Layout in tools such as HTML is implicit, when you add something into the UI it is
automatically placed correctly. Other tools such as Codename One use explicit layout,
that means you have to explicitly request the UI to layout itself after making changes!
Like many such rules exceptions occur. E.g. if the device is rotated
or window size changes a layout will occur automatically.
When adding a component to a UI that is already visible, the component will not show
by default.
When adding a component to a form which isnt shown on the screen,
there is no need to tell the UI to repaint or reflow. This happens
implicitly.
The chief advantage of explicit layout is performance.
E.g. imagine adding 100 components to a form. If the form was laid out automatically,
layout would have happened 100 times instead of once when adding was finished. In
fact layout reflows are often considered the #1 performance issue for HTML/JavaScript
applications.
Smart layout reflow logic can alleviate some of the pains of the
automatic layout reflows however since the process is implicit its
almost impossible to optimize complex usages across browsers/
devices. A major JavaScript performance tip is to use absolute
positioning which is akin to not using layouts at all!
That is why, when you add components to a form that is already showing, you should
invoke revalidate() or animate the layout appropriately. This also enables the
layout animation behavior explained below.
344
6.2.Layout Animations
To understand animations you need to understand a couple of things about
Codename One components. When we add a component to a container, its
generally just added but not positioned anywhere. A novice might notice the
setX / setY / setWidth / setHeight methods on a component and just try to
position it absolutely.
This wont work since these methods are meant for the layout manager, which is
implicitly invoked when a form is shown (internally in Codename One). The layout
manager uses these methods to position/size the components based on the hints given
to it.
If you add components to a form that is currently showing, it is your responsibility to
invoke revalidate / layoutContainer to arrange the newly added components
(see Layout Reflows).
animateLayout() method is a fancy form of revalidate that animates the
components into their laid out position. After changing the layout & invoking this method
the components move to their new sizes/positions seamlessly.
This sort of behavior creates a special case where setting the size/position makes
sense. When we set the size/position in the demo code here we are positioning the
components at the animation start position above the frame.
Form hi = new Form("Layout Animations", new BoxLayout(BoxLayout.Y_AXIS));
Button fall = new Button("Fall");
fall.addActionListener((e) -> {
b.setWidth(fall.getWidth());
b.setHeight(fall.getHeight());
b.setY(-fall.getHeight());
hi.add(b);
hi.getContentPane().animateLayout(20000);
});
hi.add(fall);
There are a couple of things that you should notice about this example:
345
6.2.1.Unlayout Animations
While layout animations are really powerful effects for adding elements into the UI and
drawing attention to them. The inverse of removing an element from the UI is often
more important. E.g. when we delete or remove an element we want to animate it out.
Layout animations dont really do that since they will try to bring the animated item
into place. What we want is the exact opposite of a layout animation and that is the
"unlayout animation".
The "unlayout animation" takes a valid laid out state and shifts the components to an
invalid state that we defined in advance. E.g. we can fix the example above to flip the
"fall" button into a "rise" button when the buttons come into place and this will allow the
buttons to float back up to where they came from in the exact reverse order.
An unlayout animation always leaves the container in an invalidated
state.
if(hi.getContentPane().getComponentCount() == 1) {
fall.setText("Rise");
}
hi.getContentPane().animateLayout(20000);
} else {
fall.setText("Fall");
346
c.setY(-fall.getHeight());
hi.getContentPane().animateUnlayoutAndWait(20000, 255);
hi.removeAll();
hi.add(fall);
hi.revalidate();
});
hi.add(fall);
You will notice some similarities with the unlayout animation but the differences
represent the exact opposite of the layout animation:
We loop over existing components (not newly created ones)
We set the desired end position not the desired starting position
We used the AndWait variant of the animate unlayout call. We could have used
the async call as well.
After the animation completes we need to actually remove the elements since
the UI is now in an invalid position with elements outside of the screen but still
physically there!
boolean t = !toHide.isHidden();
});
toHide.setHidden(t);
toHide.getParent().animateLayoutAndWait(200);
toHide.setVisible(!t);
hide.setEnabled(true);
6.2.3.Synchronicity In Animations
Most animations have two or three variants:
Standard animation e.g. animateLayout(int)
And wait variant e.g. animateLayoutAndWait(int)
Callback variant e.g. animateUnlayout(int, int, Runnable)
The standard animation is invoked when we dont care about the completion of the
animation. We can do this for a standard animation.
The unlayout animations dont have a standard variant. Since they
leave the UI in an invalid state we must always do something once
the animation completes so a standard variant makes no sense
The AndWait variant blocks the calling thread until the animation completes. This
is really useful for sequencing animations one after the other e.g this code from the
kitchen sink demo:
arrangeForInterlace(effects);
effects.animateUnlayoutAndWait(800, 20);
effects.animateLayoutFade(800, 20);
348
The callback variant is similar to the invokeAndBlock variant but uses a more
conventional callback semantic which is more familiar to some developers. It accepts a
Runnable callback that will be invoked after the fact. E.g. we can change the unlayout
call from before to use the callback semantics as such:
hi.getContentPane().animateUnlayout(20000, 255, () -> {
hi.removeAll();
hi.add(fall);
hi.revalidate();
});
cmp.setX(-cmp.getWidth());
} else {
1
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
349
} else {
}
cmp.setX(-cmp.getWidth());
}
}
boxContainer.setShouldCalcPreferredSize(true);
boxContainer.animateHierarchyFade(3000, 30);
All the animations go thru a per-form queue: the AnimationManager . This effectively
prevents two animations from mutating the UI in parallel so we wont have collisions
between two conflicting sides. Things get more interesting when we try to do something
like this:
cnt.add(myButton);
if(componentCount == cnt.getComponentCount()) {
}
The reason this happens is that the second remove gets postponed to the end of the
animation so it wont break the animation. This works for remove and add operations
3
on a Container as well as other animations.
The simple yet problematic fix would be:
cnt.add(myButton);
2
3
https://www.codenameone.com/javadoc/com/codename1/ui/AnimationManager.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
350
if(componentCount == cnt.getComponentCount()) {
}
Events come in constantly during the run of the EDT , so an event might come in that
might trigger an animation in your code. Even if you are on the EDT keep in mind that
you dont actually block it and an event might come in.
In those cases an animation might start and you might be unaware of that animation
and it might still be in action when you expect remove to work.
cnt.animateLayout(300);
cnt.getAnimationManager().flushAnimation(() -> {
cnt.removeComponent(myButton);
if(componentCount == cnt.getComponentCount()) {
});
351
super.animate();
}
}
return true;
return super.animate();
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Animation.html
352
6.4.Transitions
Transitions allow us to replace one component with another, most typically forms or
dialogs are replaced with a transition however a transition can be applied to replace
any arbitrary component.
Developers can implement their own custom transition and install it to components
7
In/Out Transitions
When defining a transition we define the entering transition and the exiting
transition. For most cases only one of those is necessary and we default to the
exiting (out transition) as a convention.
So for almost all cases the method setFormTransitonIn should go unused.
That API exists for some elaborate custom transitions that might need to have a
special effect both when transitioning in and out of a specific form. However, most
of these effects are easier to achieve with layout animations (e.g. components
dropping into place etc.).
In the case of Dialog the transition in shows its appearance and the transition
out shows its disposal. So in that case both transitions make a lot of sense.
Back/Forward Transitions
Transitions have a direction and can all be played either in incoming or outgoing
direction. A transition can be flipped (played in reverse) when we use an RTL
7
8
9
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Transition.html
https://www.codenameone.com/javadoc/com/codename1/ui/animations/CommonTransitions.html
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/LookAndFeel.html
353
10
6.4.1.Replace
To apply a transition to a component we can just use the Container.replace()
method as such:
Form hi = new Form("Replace", new BoxLayout(BoxLayout.Y_AXIS));
Button replace = new Button("Replace Pending");
hi.add(replace);
replace.addActionListener((e) -> {
replace.getParent().replaceAndWait(replace, replaceDestiny,
CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL,
true, 800));
replaceDestiny.getParent().replaceAndWait(replaceDestiny, replace,
CommonTransitions.createUncover(CommonTransitions.SLIDE_VERTICAL,
true, 800));
});
10
354
6.4.2.Slide Transitions
The slide transitions are used to move the Form/Component in a sliding motion to
the side or up/down. There are 4 basic types of slide transitions:
1. Slide - the most commonly used transition
2. Fast Slide - historically this provided better performance for old device types. It is
no longer recommended for newer devices
3. Slide Fade - the iOS default where the title area features a fade transition
4. Cover/Uncover - a type of slide transition where only the source or destination form
slides while the other remains static in place
The code below demonstrates the usage of all the main transitions:
Toolbar.setGlobalToolbar(true);
TextArea.NUMERIC);
CheckBox horizontal = CheckBox.createToggle("Horizontal");
pick.addActionListener((e) -> {
String s = pick.getSelectedString().toLowerCase();
horizontal.setEnabled(s.equals("slide") || s.indexOf("cover") > -1);
});
horizontal.setSelected(true);
hi.add(showTransition).
add(pick).
add(duration).
355
bg = dest.getContentPane().getUnselectedStyle();
bg.setBgTransparency(255);
bg.setBgColor(0xff);
dest.setBackCommand(
dest.getToolbar().addCommandToLeftBar("Back", null, (e) ->
hi.showBack()));
showTransition.addActionListener((e) -> {
int h = CommonTransitions.SLIDE_HORIZONTAL;
if(!horizontal.isSelected()) {
}
h = CommonTransitions.SLIDE_VERTICAL;
switch(pick.getSelectedString()) {
case "Slide":
hi.setTransitionOutAnimator(CommonTransitions.createSlide(h,
true, duration.getAsInt(3000)));
dest.setTransitionOutAnimator(CommonTransitions.createSlide(h,
true, duration.getAsInt(3000)));
break;
case "SlideFade":
hi.setTransitionOutAnimator(CommonTransitions.createSlideFadeTitle(true,
duration.getAsInt(3000)));
dest.setTransitionOutAnimator(CommonTransitions.createSlideFadeTitle(true,
duration.getAsInt(3000)));
break;
case "Cover":
hi.setTransitionOutAnimator(CommonTransitions.createCover(h,
true, duration.getAsInt(3000)));
dest.setTransitionOutAnimator(CommonTransitions.createCover(h,
true, duration.getAsInt(3000)));
break;
case "Uncover":
hi.setTransitionOutAnimator(CommonTransitions.createUncover(h,
true, duration.getAsInt(3000)));
dest.setTransitionOutAnimator(CommonTransitions.createUncover(h, true,
duration.getAsInt(3000)));
break;
case "Fade":
hi.setTransitionOutAnimator(CommonTransitions.createFade(duration.getAsInt(3000)));
356
dest.setTransitionOutAnimator(CommonTransitions.createFade(duration.getAsInt(3000)));
break;
case "Flip":
hi.setTransitionOutAnimator(new FlipTransition(-1,
duration.getAsInt(3000)));
dest.setTransitionOutAnimator(new FlipTransition(-1,
duration.getAsInt(3000)));
break;
}
dest.show();
});
hi.show();
Figure 6.1. The slide transition moves both incoming and outgoing
forms together
357
Figure 6.3. Slide fade fades in the destination title while sliding the
content pane it is the default on iOS
SlideFade is problematic without a title area. If you have a Form
that lacks a title area we would recommend to disable SlideFade
at least for that Form .
Figure 6.4. With cover transitions the source form stays in place as
it is covered by the destination. This transition can be played both
horizontally and vertically
358
The FlipTransition is also pretty simple but unlike the others it isnt a part of the
CommonTransitions . It has its own FlipTransition class.
This transition looks very different on devices as it uses native
perspective transforms available only there
https://www.codenameone.com/javadoc/com/codename1/ui/animations/FlipTransition.html
359
6.4.4.Bubble Transition
BubbleTransiton
growth motion.
12
The BubbleTransition accepts the component that will grow into the bubble effect
as one of its arguments. Its generally designed for Dialog transitions although it
could work for more creative use cases:
The code below manipulates styles and look. This is done to make
the code more "self contained". Real world code should probably use
the theme
Form hi = new Form("Bubble");
showBubble.setName("BubbleButton");
Style buttonStyle = showBubble.getAllStyles();
buttonStyle.setBorder(Border.createEmpty());
buttonStyle.setFgColor(0xffffff);
buttonStyle.setBgPainter((g, rect) -> {
g.setColor(0xff);
int actualWidth = rect.getWidth();
yPos = rect.getY();
xPos = rect.getX() + (actualWidth - actualHeight) / 2;
size = actualHeight;
} else {
}
g.setAntiAliased(true);
g.fillArc(xPos, yPos, size, size, 0, 360);
});
hi.add(showBubble);
hi.setTintColor(0);
showBubble.addActionListener((e) -> {
12
https://www.codenameone.com/javadoc/com/codename1/ui/animations/BubbleTransition.html
360
BubbleTransition(500, "BubbleButton"));
dlg.setTransitionOutAnimator(new
BubbleTransition(500, "BubbleButton"));
dlg.setDisposeWhenPointerOutOfBounds(true);
dlg.getTitleStyle().setFgColor(0xffffff);
});
hi.show();
361
362
6.4.5.Morph Transitions
Androids material design has a morphing effect where an element from the previous
form (activity) animates into a different component on a new activity. Codename One
13
has a morph effect in the Container class but it doesnt work as a transition between
forms and doesnt allow for multiple separate components to transition at once.
14
effect coupled with a fade to the rest of the UI (see Figure 6.9, Morph Transition).
Since the transition is created before the form exists we cant reference explicit
components within the form when creating the morph transition (in order to indicate
which component becomes which) so we need to refer to them by name. This means we
need to use setName(String) on the components in the source/destination forms
so the transition will be able to find them.
Form demoForm = new Form(currentDemo.getDisplayName());
demoForm.setScrollable(false);
demoForm.setLayout(new BorderLayout());
demoLabel.setIcon(currentDemo.getDemoIcon());
demoLabel.setName("DemoLabel");
demoForm.addComponent(BorderLayout.NORTH, demoLabel);
demoForm.addComponent(BorderLayout.CENTER, wrapInShelves(n));
....
demoForm.setBackCommand(backCommand);
demoForm.setTransitionOutAnimator(MorphTransition.create(3000).morph(currentDemo.getDisp
f.setTransitionOutAnimator(MorphTransition.create(3000).morph(currentDemo.getDisplayName
demoForm.show();
13
14
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
https://www.codenameone.com/javadoc/com/codename1/ui/animations/MorphTransition.html
363
6.4.6.SwipeBackSupport
iOS7+ allows swiping back one form to the previous form, Codenmae One has an API
to enable back swipe transition:
SwipeBackSupport.bindBack(Form currentForm, LazyValue<Form> destination);
That one command will enable swiping back from currentForm . LazyValue
us to pass a value lazily:
15
allows
This effectively allows us to pass a form and only create it as necessary (e.g. for a GUI
builder app we dont have the actual previous form instance), notice that the arguments
arent used for this case but will be used in other cases.
The code below should work for the transition sample above. Notice that this API was
designed to work with "Slide Fade" transition and might have issues with other transition
types:
SwipeBackSupport.bindBack(dest, (args) -> hi);
15
https://www.codenameone.com/javadoc/com/codename1/util/LazyValue.html
364
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
Normally, every call you receive from Codename One will occur on the EDT. E.g. every
event, calls to paint(), lifecycle calls (start etc.) should all occur on the EDT.
This is pretty powerful, however it means that as long as your code is processing
nothing else can happen in Codename One!
If your code takes too long to execute then no painting or
event processing will occur during that time, so a call to
Thread.sleep() will actually stop everything!
The solution is pretty simple, if you need to perform something that requires intensive
CPU you can spawn a thread.
365
One
includes
methods
help
in
these
situations:
isEDT() ,
callSeriallyAndWait(Runnable) .
in
the
Display
class
to
callSerially(Runnable )
&
isEDT() is useful for generic code that needs to test whether the current code is
executing on the EDT.
Display.getInstance().callSerially(new Runnable() {
public void run() {
});
resultLabel.setText(res);
You can write this code more concisely using Java 8 lambda code
as such:
https://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
366
Display.getInstance().callSeriallyAndWait(() -> {
}
otherMethod();
368
http://foxtrot.sourceforge.net/
369
doOperationB();
}
}).start();
doOperationC();
Unfortunately, this means that operation C will happen in parallel to operation B which
might be a problem
E.g. instead of using operation names lets use a more "real world" example:
updateUIToLoadingStatus();
readAndParseFile();
updateUIWithContentOfFile();
Notice that the first and last operations must be conducted on the EDT but the
middle operation might be really slow! Since updateUIWithContentOfFile needs
readAndParseFile to occur before it starts doing the new thread wont be enough.
A simplistic approach is to do something like this:
updateUIToLoadingStatus();
new Thread() {
readAndParseFile();
updateUIWithContentOfFile();
}
}).start();
readAndParseFile();
370
});
updateUIWithContentOfFile();
}
}).start();
This is perfectly legal and would work reasonably well, however it gets complicated as
we add more and more features that need to be chained serially after all these are just
3 methods!
Invoke and block solves this in a unique way you can get almost the exact same
behavior by using this:
updateUIToLoadingStatus();
Display.getInstance().invokeAndBlock(new Runnable() {
public void run() {
}
readAndParseFile();
});
updateUIWithContentOfFile();
Invoke and block effectively blocks the current EDT in a legal way. It spawns a separate
thread that runs the run() method and when that run method completes it goes back
to the EDT.
All events and EDT behavior still work while invokeAndBlock is running, this is
because invokeAndBlock() keeps calling the main thread loop internally.
Notice that invokeAndBlock comes at a slight performance
penalty. Also notice that nesting invokeAndBlock calls (or over
using them) isnt recommended.
However, they are very convenient when working with multiple
threads/UI.
371
Notice that the dialog show method will block the calling thread until the user clicks
OK or Not OK
Other APIs such as NetworkManager.addToQueueAndWait()
also make use of this feature. Pretty much every "AndWait" method
or blocking method uses this API internally!
To explain how invokeAndBlock works we can return to the sample above of how the
EDT works:
while(codenameOneRunning) {
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
performEventCallbacks();
performCallSeriallyCalls();
drawGraphicsAndAnimations();
sleepUntilNextEDTCycle();
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html
372
373
Chapter8.Monetization
Codename One tries to make the lives of software developers easier by integrating
several forms of built-in monetization solutions such as ad network support, in-apppurchase etc.
1
A lot of the monetization options are available as 3rd party cn1libs that you can install
thru the Codename One website.
https://www.codenameone.com/cn1libs.html
https://apps.admob.com/?pli=1#monetize/adunit:create
374
Monetization
1. Googles in app purchase
2. Apples in app purchase
3. Mobile payments for physical goods
While all 3 approaches end up with the developer getting paid, all 3 take a different
approach to the same idea. Google and Apple work with products which you can
define and buy through their respective stores. You need to define the product in the
development environment and then send the user to purchase said product.
Once the product is purchased you receive an event that the purchase was completed
and you can act appropriately. On the other hand mobile payments are just a transfer
of a sum of money.
Both Googles and Apples stores prohibit the sale of physical goods via the stores,
so a mobile payment system needs to be used for those cases. This is where the
similarity ends between the Google & Apple approach. Google expects developers to
build their own storefront and provides developers with an API to extract the data in
order to construct said storefront. Apple expects the developers to open its storefront
to perform everything.
We tried to encode all 3 approaches into the purchase API which means you would
need to handle all 3 cases when working. Unfortunately these things are very hard to
simulate and can only be properly tested on the device.
So to organize the above we have:
1. Managed payments - payments are handled by the platform. We essentially buy
an item not transfer money (in app purchase).
2. Manual payments - we transfer money, there are no items involved.
final Purchase p = Purchase.getInAppPurchase();
if(p != null) {
if(p.isManualPaymentSupported()) {
sendMoney.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
p.pay(Double.parseDouble(tf.getText()), "USD");
375
Monetization
}
});
purchaseDemo.addComponent(tf);
purchaseDemo.addComponent(sendMoney);
if(p.isManagedPaymentSupported()) {
buy.addActionListener(new ActionListener() {
p.purchase(id);
});
purchaseDemo.addComponent(buy);
} else {
device"));
The item names in the demo code above should be hard coded and added to the
appropriate stores inventory. Which is a very platform specific process for iTunes and
Google play. Once this is done you should be able to issue a purchase request either
in the real or the sandbox store.
376
The Graphics class is responsible for drawing basics, shapes, images and text, it is
never instantiated by the developer and is always passed on by the Codename One
API.
You can gain access to a Graphics using one of the following methods:
2
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
377
378
g.setColor(0xff0000);
// paint the screen in red
screen
});
hi.show();
379
Figure 9.1. Hi world demo code, notice that the blue bar on top is
the iOS7+ status bar
9.2.Glass Pane
The GlassPane `in Codename One is inspired by the Swing
`GlassPane & LayeredPane with quite a few twists. We tried to imagine how Swing
developers would have implemented the glass pane knowing what they do now about
painters and Swings learning curve. But first: what is the glass pane?
A typical Codename One application is essentially composed of 3 layers (this is a
gross simplification though), the background painters are responsible for drawing the
380
So if we have a form with a Button and text drawn on top using the Forms paint
method it would get erased whenever the button gets focus.
The glass pane is called whenever a component gets painted, it only paints within the
clipping region of the component hence it wont break the rest of the components on
the Form which werent modified.
You can set a painter on a form using code like this:
7
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
381
hi.setGlassPane(new Painter() {
@Override
Style s = UIManager.getInstance().getComponentStyle("Label");
s.setFgColor(0xff0000);
s.setBgTransparency(0);
Image warningImage = FontImage.createMaterial(FontImage.MATERIAL_WARNING,
s).toImage();
TextField tf1 = new TextField("My Field");
tf1.getAllStyles().setMarginUnit(Style.UNIT_TYPE_DIPS);
tf1.getAllStyles().setMargin(5, 5, 5, 5);
hi.add(tf1);
hi.setGlassPane((g, rect) -> {
int x = tf1.getAbsoluteX() + tf1.getWidth();
int y = tf1.getAbsoluteY();
x -= warningImage.getWidth() / 2;
y += (tf1.getHeight() / 2 - warningImage.getHeight() / 2);
g.drawImage(warningImage, x, y);
});
hi.show();
https://www.codenameone.com/javadoc/com/codename1/ui/painter/PainterChain.html
382
Figure 9.3. The glass pane draws the warning sign on the border of
the component partially peeking out
9.4.Device Support
Shapes and transforms are available on most smartphone platforms with some caveats
for the current Windows Phone port.
Notice that perspective transform is missing from the desktop/simulator port.
Unfortunately there is no real equivalent to perspective transform in JavaSE that we
could use.
The app works by simply keeping a GeneralPath in memory, and continually adding
points as bezier curves. Whenever a point is added, the path is redrawn to the screen.
The center of the app is the DrawingCanvas class, which extends Component
public class DrawingCanvas extends Component {
9
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html
10
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
383
10
// To be written
@Override
);
g.setColor(strokeColor);
// Draw the shape
g.drawShape(p, stroke);
}
@Override
addPoint(x-getParent().getAbsoluteX(), ygetParent().getAbsoluteY());
}
}
https://www.codenameone.com/javadoc/com/codename1/ui/
Component.html#paintBackground(com.codename1.ui.Graphics)
12
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html
13
https://www.codenameone.com/javadoc/com/codename1/ui/geom/Shape.html
384
9.5.1.Implementing addPoint()
The addPoint method is designed to allow us to add points to the drawing. A simple
implementation that uses straight lines rather than curves might look like this:
private float lastX = -1;
private float lastY = -1;
public void addPoint(float x, float y) {
if (lastX == -1) {
} else {
p.lineTo(x, y);
}
lastX = x;
lastY = y;
repaint();
385
386
16
follows:
} else {
}
odd = !odd;
lastX = x;
lastY = y;
repaint();
This change should be fairly straight forward except, perhaps, the business with the
odd variable. Since quadratic curves require two points (in addition to the implied
14
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html#quadTo(float,
%20float,%20float,%20float)
15
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html#curveTo(float,
%20float,%20float,%20float,%20float,%20float)
16
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html
17
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html#quadTo(float,
%20float,%20float,%20float)
387
388
389
Figure 9.5. Result of quadTo example
supports the shape API. If I were to run this code on a device that doesnt
18
support the Shape
API, it would just draw a blank canvas where I expected
my shape to be drawn. You can fall back gracefully if you make use of the
19
Graphics.isShapeSupported()
method. E.g.
@Override
if ( g.isShapeSupported() ){
} else {
9.6.Transforms
20
The Graphics class has included limited support for 2D transformations for some
time now including scaling, rotation, and translation:
scale(x,y) : Scales drawing operations by a factor in each direction.
translate(x,y) : Translates drawing operations by an offset in each direction.
rotate(angle) : Rotates about the origin.
rotate(angle, px, py) : Rotates about a pivot point.
scale() and rotate() methods are only available on platforms
that support Affine transforms. See table X for a compatibility list.
18
19
20
https://www.codenameone.com/javadoc/com/codename1/ui/geom/Shape.html
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html#isShapeSupported()
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html
390
9.6.1.Device Support
As of this writing, not all devices support transforms (i.e. scale() and rotate() ).
The following is a list of platforms and their respective levels of support.
Affine Supported
Simulator
Yes
iOS
Yes
Android
Yes
JavaScript
Yes
J2ME
No
No
Windows Phone
No (pending)
21
e.g.
public void paint(Graphics g){
if ( g.isAffineSupported() ){
} else {
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html
391
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
392
double cX = getX()+getWidth()/2;
double cY = getY()+getHeight()/2;
//Tick Styles
// short tick
// at 5-minute intervals
if ( i % 15 == 0 ){
} else if ( i % 5 == 0 ){
len = medTickLen;
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html
393
(float)(cX+Math.cos(angleFrom3)*r),
);
(float)(cY-Math.sin(angleFrom3)*r)
(float)(cX+Math.cos(angleFrom3)*(r-len)),
);
(float)(cY-Math.sin(angleFrom3)*(r-len))
394
// Calculate the position along the edge of the clock where the number
should
// be drawn
int tx = (int)(Math.cos(angleFrom3)*(r-longTickLen));
int ty = (int)(-Math.sin(angleFrom3)*(r-longTickLen));
// For 6 and 12 we will shift number slightly so they are more even
if ( i == 6 ){
ty -= charHeight/2;
} else if ( i == 12 ){
}
ty += charHeight/2;
g.translate(-tx, -ty);
396
Now, we should have a clock with tick marks and numbers as shown below:
24
https://www.codenameone.com/javadoc/com/codename1/ui/geom/GeneralPath.html
397
GeneralPath object for each hand. For the positioning/angle of each, I will employ
the following strategy:
1. Draw the hand at the clock center pointing toward 12 (straight up).
2. Translate the hand slightly down so that it overlaps the center.
3. Rotate the hand at the appropriate angle for the current time, using the clock center
as a pivot point.
Drawing the Second Hand:
For the "second" hand, we will just use a simple line from the clock center to the inside
edge of the medium tick mark at the 12 oclock position.
GeneralPath secondHand = new GeneralPath();
secondHand.moveTo((float)cX, (float)cY);
secondHand.lineTo((float)cX, (float)(cY-(r-medTickLen)));
And we will translate it down slightly so that it overlaps the center. This translation will be
performed on the GeneralPath object directly rather than through the Graphics
context:
Shape translatedSecondHand = secondHand.createTransformedShape(
Transform.makeTranslation(0f, 5)
);
);
g.resetAffine();
minuteHand.lineTo((float)cX+6, (float)cY);
minuteHand.lineTo((float)cX+2, (float)(cY-(r-tickLen)));
minuteHand.lineTo((float)cX-2, (float)(cY-(r-tickLen)));
minuteHand.lineTo((float)cX-6, (float)cY);
399
hourHand.lineTo((float)cX+4, (float)cY);
hourHand.lineTo((float)cX+1, (float)(cY-(r-longTickLen)*0.75));
hourHand.lineTo((float)cX-1, (float)(cY-(r-longTickLen)*0.75));
hourHand.lineTo((float)cX-4, (float)cY);
hourHand.closePath();
if ( System.currentTimeMillis()/1000 != lastRenderedTime/1000){
currentTime.setTime(System.currentTimeMillis());
}
}
return true;
return false;
This method will be invoked on each "pulse" of the EDT. It checks the last time the
clock was rendered and returns true only if the clock hasnt been rendered in the
current "time second" interval. Otherwise it returns false. This ensures that the clock
will only be redrawn when the time changes.
getComponentForm().registerAnimated(this);
getComponentForm().deregisterAnimated(this);
So the code to instantiate the clock, and start the animation would be something like:
AnalogClock clock = new AnalogClock();
parent.addComponent(clock);
clock.start();
402
9.9.Shape Clipping
Clipping is one of the core tenants of graphics programming, you define the boundaries
for drawing and when you exceed said boundaries things arent drawn. Shape clipping
allows us to clip based on any arbitrary Shape and not just a rectangle, this allows
some unique effects generated in runtime.
E.g. this code allows us to draw a rather complex image of duke:
Image duke = null;
try {
// duke.png is just the default Codename One icon copied into place
duke = Image.createImage("/duke.png");
} catch(IOException err) {
}
Log.e(err);
(((float)rect.getY()) / heightRatio));
g.setClip(path);
g.setAntiAliased(true);
g.drawImage(finalDuke, 0, 0, 50, 100);
403
(((float)rect.getY()) / heightRatio));
});
g.resetAffine();
hi.show();
Figure 9.9. Shape Clipping used to clip the image of duke within the
given shape
Notice that this functionality isnt available on all platforms so
you normally need to test if shaped clipping is supported using
25
isShapeClipSupported() .
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html#isShapeClipSupported--
404
26
https://www.codenameone.com/javadoc/com/codename1/ui/Graphics.html
405
g.translate(currX, currY);
9.10.1.Relative Coordinates
Usually, when you are drawing onto a Graphics context, you are doing so within
the context of a Components paint() method (or one of its variants). In this case,
you generally dont care what the exact screen coordinates are of your drawing. You
are only concerned with their relative location within the coordinate. You can leave the
positioning (and even sizing) of the coordinate up to Codename One. Thank you for
reading.
27
https://www.codenameone.com/javadoc/com/codename1/ui/geom/Rectangle.html
406
g.setColor(0x0000ff);
g.drawRect(getX()+5, getY()+5, getWidth()-10, getHeight()-10);
407
408
28
super("Rectangle Rotations");
for ( int i=0; i< 10; i++ ){
this.addComponent(new RectangleComponent());
28
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/BorderLayout.html
410
411
uses the screen origin as the pivot point for the rotation. Components nearer to this
pivot point will experience a less dramatic effect than components farther from it. In our
case, the rotation has caused all rectangles except the first one to be rotated outside
the bounds of their containing component - so they are being clipped. A more sensible
solution for our component would be to place the rotation pivot point somewhere inside
the component. That way all of the components would look the same. Some possibilities
would be:
Top Left Corner:
public void paint(Graphics g) {
g.setColor(0x0000ff);
g.rotate((float)(Math.PI/4.0), getAbsoluteX(),
getAbsoluteY());
g.drawRect(getX() + 5, getY() + 5, getWidth() - 10,
getHeight() - 10);
g.rotate(-(float) (Math.PI / 4.0), getAbsoluteX(),
getAbsoluteY());
}
412
413
(float)(Math.PI/4.0),
getAbsoluteX()+getWidth()/2,
getAbsoluteY()+getHeight()/2
);
g.drawRect(getX() + 5, getY() + 5, getWidth() - 10, getHeight() - 10);
g.rotate(
-(float)(Math.PI/4.0),
);
getAbsoluteX()+getWidth()/2,
getAbsoluteY()+getHeight()/2
414
415
Figure 9.14. Rotating the rectangle with the center pivot point
9.10.3.Event Coordinates
The coordinate system and event handling are closely tied. You can listen for touch
events on a component by overriding the pointerPressed(x,y) method. The
coordinates received in this method will be absolute screen coordinates, so you
may need to do some conversions on these coordinates before using them in your
drawXXX() methods.
E.g. a pointerPressed() callback method can look like this:
public void pointerPressed(int x, int y) {
}
addPoint(x-getParent().getAbsoluteX(), y-getParent().getAbsoluteY());
In this case we translated these points so that they would be relative to the origin of
the parent component. This is because the drawXXX() methods for this component
take coordinates relative to the parent component.
9.11.Images
Codename One has quite a few image types: loaded, RGB (builtin), RGB
(Codename One), Mutable, EncodedImage, SVG, MultiImage, FontImage &
Timeline. There are also URLImage, FileEncodedImage, FileEncodedImageAsync,
StorageEncodedImage /Async that will be covered in the IO section.
All image types are mostly seamless to use and will just work with drawImage and
various image related image APIs for the most part with caveats on performance etc.
For animation images the code must invoke the animate()
method on the image (this is done automatically by Codename One
when placing the image as a background or as an icon!
You only need to do it if you invoke drawImage in code rather than
use a builtin component).
Performance and memory wise you should read the section below carefully and be
aware of the image types you use. The Codename One designer tries to conserve
416
9.11.1.Loaded Image
This is the basic image you get when loading an image from the jar or
29
30
network using Image.createImage(String) , Image.createImage(InputStream)
&
31
Image.createImage(byte array,int,int) ,
Some other APIs might return this image type but those APIs do
so explicitly!
In some platforms calling getGraphics() on an image like this will throw an
exception as its immutable). This is true for almost all other images as well.
This restriction might not apply for all platforms.
The image is stored in RAM based on device logic and should be reasonably efficient
in terms of drawing speed. However, it usually takes up a lot of RAM.
To calculate the amount of RAM taken by a loaded image we use the following formula:
Image Width * Image Height * 4 = Size In RAM in Bytes
417
Internal
This is a close cousin of the loaded image. This image is created using the method
32
Image.createImage(int array, int, int) and receives the AARRGGBB data to form the
image. Its more efficient than the Codename One RGB image but cant be modified,
at least not on the pixel level.
The goal of this image type is to provide an easy way to render RGB data that
isnt modified efficiently at platform native speeds. Its technically a standard "Loaded
Image" internally.
RGBImage class
RGBImage
33
On most platforms this is quite inefficient but for some pixel level manipulations there
is just no other way.
An RGBImage is constructed with an int array ( int[] ) that includes
width*height elements. You can then modify the colors and alpha channel directly
within the array and draw the image to any source using standard image drawing APIs.
This is very inefficient in terms of rendering speed and memory
overhead. Only use this technique if there is absolutely no other way!
9.11.3.EncodedImage
34
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html#createImage-int:A-int-inthttps://www.codenameone.com/javadoc/com/codename1/ui/RGBImage.html
https://www.codenameone.com/javadoc/com/codename1/ui/EncodedImage.html
418
EncodedImage Locking
Naturally loading the image is more expensive so we want the images that are on
the current form to remain in cache (otherwise GC will thrash a lot). Thats where
lock() kicks in, when lock() is active we keep a hard reference to the actual
native image so it wont get GCd. This significantly improves performance!
35
36
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
https://www.codenameone.com/javadoc/com/codename1/ui/EncodedImage.html#createFromImage-
com.codename1.ui.Image-boolean-
419
9.11.4.MultiImage
Multi images dont physically exist as a concept within the Codename One API so
there is no way to actually create them and they are in no way distinguishable from
EnclodedImage .
The only builtin support for multi images is in the resource file loading logic where a
MultiImage is decoded and only the version that matches the current DPI is physically
loaded. From that point on user code can treat it like any other EnclodedImage .
9-image borders use multi images by default to keep their appearance more refined
on the different DPIs.
FontImage allows using an icon font as if it was an image. You can specify the
character, color and size and then treat the FontImage as if its a regular image. The
huge benefits are that the font image can adapt to platform conventions in terms of
color and easily scale to adapt to DPI.
38
You can generate icon fonts using free tools on the internet such as this . Icon fonts
are a remarkably simple and powerful technique to create a small, modern applications.
Icon fonts can be created in 2 basic ways the first is explicitly by defining all of the
elements within the font.
Form hi = new Form("Icon Font");
https://www.codenameone.com/javadoc/com/codename1/ui/FontImage.html
http://fontello.com/
420
Figure 9.15. Icon font from material design icons created with the
fixed size of display width
The samples use the builtin material design icon font. This is for
convenience so the sample will work out of the box, for everyone.
However you should be able to do this with any arbitrary icon font off
the internet as long as its a valid TTF file.
421
There are two versions of this method, the first one expects the Style object to have
the correct icon font set to its font attribute. The second accepts a Font object as an
argument. The latter is useful for a case where you want to reuse the same Style
object that you defined for a general UI element e.g. we can set an icon for a Button
like this and it will take up the style of the Button :
Form hi = new Form("Icon Font");
myButton.setIcon(FontImage.create("\uE161", myButton.getUnselectedStyle(),
materialFont));
hi.add(myButton);
hi.show();
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/Style.html
422
myButton.setIcon(FontImage.createMaterial(FontImage.MATERIAL_SAVE,
myButton.getUnselectedStyle()));
hi.add(myButton);
FontImage.setMaterialIcon(myButton, FontImage.MATERIAL_SAVE);
hi.add(myButton);
This will produce the same result for slightly shorter syntax.
FontImage can conflict with some complex APIs that expect a
"real" image underneath. Some odd issues can often be resolved
40
https://design.google.com/icons/
423
9.11.6.Timeline
Timelines allow rudimentary animation and enable GIF importing using the Codename
One Designer. Effectively a timeline is a set of images that can be moved rotated,
scaled & blended to provide interesting animation effects. It can be created manually
41
using the Timeline class.
9.11.7.Image Masking
Image masking allows us to manipulate images by changing the opacity of an
image according to a mask image. The mask image can be hardcoded or generated
dynamically, it is then converted to a Mask object that can be applied to any image.
Notice that the masking process is computationally intensive, it should be done once
and cached/saved.
The code below can convert an image to a rounded image:
Toolbar.setGlobalToolbar(true);
hi.add(BorderLayout.CENTER, picture);
hi.getUnselectedStyle().setBgColor(0xff0000);
hi.getUnselectedStyle().setBgTransparency(255);
Style s = UIManager.getInstance().getComponentStyle("TitleCommand");
Image camera = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s);
hi.getToolbar().addCommandToRightBar("", camera, (ev) -> {
try {
Image capturedImage =
Image.createImage(Capture.capturePhoto(width, -1));
Image roundMask = Image.createImage(width,
capturedImage.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, width, width, 0, 360);
Object mask = roundMask.createMask();
capturedImage = capturedImage.applyMask(mask);
picture.setIcon(capturedImage);
hi.revalidate();
41
https://www.codenameone.com/javadoc/com/codename1/ui/animations/Timeline.html
424
});
Log.e(err);
Figure 9.18. Picture after the capture was complete and the resulted
image was rounded. The background was set to red so the rounding
effect will be more noticeable
Notice that this example is simplistic in order to be self contained. We often recommend
that developers ship "ready made" mask images with their application which can allow
very complex effects on the images.
425
9.11.8.URLImage
URLImage
42
image in the given URL while caching it locally. The typical adapt process scales the
image or crops it to fit into the same size which is a hard restriction because of the way
URLImage is implemented.
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
https://www.codenameone.com/javadoc/com/codename1/io/Storage.html
426
In the adapter interface and just return the processed encoded image. If you do heavy
processing (e.g. rounded edge images) you would need to convert the processed image
back to an encoded image so it can be saved. You would then also want to indicate
that this operation should run asynchronously via the appropriate method in the class.
If you need to download the file instantly and not wait for the image to appear
before download initiates you can explicitly invoke the fetch() method which will
asynchronously fetch the image from the network. Notice that the downloading will still
take time so the placeholder is still required.
Mask Adapter
A URLImage can be created with a mask adapter to apply an effect to an image. This
allows us to round downloaded images or apply any sort of masking e.g. we can adapt
the round mask code above as such:
44
45
https://www.codenameone.com/javadoc/com/codename1/ui/util/ImageIO.html
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.ImageAdapter.html
427
gr.setColor(0xffffff);
gr.fillArc(0, 0, placeholder.getWidth(), placeholder.getHeight(), 0, 360);
URLImage.ImageAdapter ada = URLImage.createMaskAdapter(roundMask);
Image i =
URLImage.createToStorage(placeholder, "fileNameInStorage", "http://xxx/
myurl.jpg", ada);
URLImage In Lists
The biggest problem with image download service is with lists. We decided to
46
attack this issue at the core by integrating URLImage
support directly into
47
48
49
GenericListCellRenderer
which means it will work with MultiList , List
&
50
ContainerList . To use this support just define the name of the component (name not
UIID) to end with _URLImage and give it an icon to use as the placeholder. This is
easy to do in the multilist by changing the name of icon to icon_URLImage then using
this in the data:
map.put("icon_URLImage", urlToActualImage);
Make sure you also set a "real" icon to the entry in the GUI builder or in handcoded
applications. This is important since the icon will be implicitly extracted and used as
the placeholder value. Everything else should be handled automatically. You can use
setDefaultAdapter & setAdapter on the generic list cell renderer to install
adapters for the images. The default is a scale adapter although we might change that
to scale fill in the future.
Style s = UIManager.getInstance().getComponentStyle("Button");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder =
EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight()
* 4), false);
46
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
47
https://www.codenameone.com/javadoc/com/codename1/ui/list/GenericListCellRenderer.html
48
https://www.codenameone.com/javadoc/com/codename1/ui/list/MultiList.html
49
https://www.codenameone.com/javadoc/java/util/List.html
50
https://www.codenameone.com/javadoc/com/codename1/ui/list/ContainerList.html
428
ml.getUnselectedButton().setIconName("icon_URLImage");
ml.getSelectedButton().setIconName("icon_URLImage");
ml.getUnselectedButton().setIcon(placeholder);
ml.getSelectedButton().setIcon(placeholder);
hi.add(BorderLayout.CENTER, ml);
return entry;
429
Figure 9.19. A URL image fetched dynamically into the list model
430
Chapter10.Events
There are two layers of event handlers in Codename One :icons: font
Most events in Codename One are routed via the high level events (e.g. action listener)
but sometimes we need access to low level events (e.g. when drawing via Graphics)
that provide more fine grained access. Typically working with the higher level events is
far more potable since it might map to different functionality on different devices.
10.1.1.Chain Of Events
Since all events fire on the EDT some complexities occur. E.g.:
We have two listeners monitoring the same event (or related events e.g. pointer event
and button click event both of which will fire when the button is touched).
When the event occurs we can run into a scenario like this:
1. First event fires
2. It shows a blocking dialog or invokes an "AndWait" API
3. Second event fires only after the dialog was dismissed!
This happens because events are processed in-order per cycle. Since the old EDT
cycle is stuck (because of the Dialog ) the rest of the events within the cycle cant
complete. New events are in a new EDT cycle so they can finish just fine!
A workaround to this issue is to wrap the code in a callSerially , you shouldnt do
this universally as this can create a case of shifting the problem to the next EDT cycle.
However, using callSerially will allow the current cycle to flush which should help.
1
https://www.codenameone.com/javadoc/com/codename1/ui/util/EventDispatcher.html
431
Events
Another workaround for the issue is avoiding blocking calls within an event chain.
10.1.2.Action Events
2
The most common high level event is the ActionListener which allows binding a
generic action event to pretty much anything. This is so ubiquitous in Codename One
that it is even used for networking (as a base class) and for some of the low level event
options.
3
b.addActionListener(new ActionListener() {
});
Notice that the click will work whether the button was touched using a mouse, finger or
keypad shortcut seamlessly with an action listener. Many components work with action
events e.g. buttons, text components, slider etc.
There are quite a few types of high level event types that are more specific to
requirements.
https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionListener.html
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
432
Events
4
You can get the event type from getEventType() , this also gives you a rather
exhaustive list of the possible event types for the action event.
Source Of Event
ActionEvent has a source object, what that source is depends heavily on the event
type. For most component based events this is the component but there are some
nuances.
The getComponent() method might not get the actual component. In case of a lead
5
6
component such as MultiButton the underlying Button will be returned and not the
MultiButton itself.
To get the component that you would logically think of as the source component use
the getActualComponent() method.
Event Consumption
An ActionEvent can be consumed, once consumed it will no longer proceed down
the chain of event processing. This is useful for some cases where we would like to
block behavior from proceeding down the path.
E.g. the event dispatch thread allows us to listen to errors on the EDT using:
Display.getInstance().addEdtErrorHandler((e) -> {
Exception err = (Exception)e.getSource();
});
// ...
This will work great but you will still get the default error message from the EDT over
that exception. To prevent the event from proceeding to the default error handling you
can just do this:
Display.getInstance().addEdtErrorHandler((e) -> {
e.consume();
Exception err = (Exception)e.getSource();
// ...
4
5
6
https://www.codenameone.com/javadoc/com/codename1/ui/events/ActionEvent.html#getEventType-https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html
https://www.codenameone.com/javadoc/com/codename1/ui/Button.html
433
Events
});
Notice that you can check if an event was already consumed using the
isConsumed() method but its pretty unlikely that you will receive a consumed event
as the system will usually stop sending it.
NetworkEvent
7
});
};
https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html
434
Events
});
These seem very similar but they have one important distinction. The latter code is
invoked on the EDT, so if data is big it might slow down processing significantly.
The ConnectionRequest is invoked on the network thread and so can process any
amount of data without slowing down the UI significantly.
10.1.3.DataChangeListener
8
TextField - the text field provides an action listener but that only "fires" when the
data input is complete. DataChangeListener fires with every key entered and
thus allows functionality such as "auto complete" and is indeed used internally in
the Codename One AutoCompleteTextField.
10
11
12
TableModel & ListModel - the model for the Table class notifies the view that
its content has changed via this event, thus allowing the UI to refresh properly.
There is a very exhaustive example of search that is implemented using the
DataChangedListener in the Toolbar section.
10.1.4.FocusListener
The focus listener allows us to track the currently "selected" or focused component. Its
not as useful as it used to be in feature phones.
You can bind a focus listener to the Component itself and receive an event when it
gained focus, or you can bind the listener to the Form and receive events for every
focus change event within the hierarchy.
8
https://www.codenameone.com/javadoc/com/codename1/ui/events/DataChangedListener.html
9
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
10
https://www.codenameone.com/javadoc/com/codename1/ui/table/TableModel.html
11
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
12
https://www.codenameone.com/javadoc/com/codename1/ui/table/Table.html
435
Events
10.1.5.ScrollListener
ScrollListener
13
necessary.
Normally scrolling is seamless and this event isnt necessary, however if developers
wish to "shrink" or "fade" an element on scrolling this interface can be used to achieve
that. Notice that you should bind the scroll listener to the actual scrollable component
and not to an arbitrary component.
E.g. in this code from the Flickr demo the Toolbar
position:
14
public CustomToolbar() {
}
super.paintComponentBackground(g);
}
g.setAlpha(a);
alpha = scrollY;
alpha = Math.max(alpha, 0);
alpha = Math.min(alpha, 255);
13
14
https://www.codenameone.com/javadoc/com/codename1/ui/events/ScrollListener.html
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
436
Events
10.1.6.SelectionListener
The SelectionListener
15
to the list view. Since list supports the ActionListener event callback its usually
the better option since its more coarse grained.
SelectionListener gets fired too often for events and that might result in a
10.1.7.StyleListener
StyleListener
16
This will trigger a style event that will eventually lead to the component being repainted.
This is quite important for the component class but not a very important event for
general user code. It is recommended that developers dont bind a style listener.
10.1.8.Event Dispatcher
When creating your own components and objects you sometimes want to broadcast
17
your own events, for that purpose Codename One has the EventDispatcher class
which saves a lot of coding effort in this regard. E.g. if you wish to provide an
18
ActionListener event for components you can just add this to your class:
private final EventDispatcher listeners = new EventDispatcher();
public void addActionListener(ActionListener a) {
}
listeners.addListener(a);
437
Events
listeners.removeListener(a);
listeners.fireActionEvent(ev);
methods
20
in
Form
19
e.g.
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
https://www.codenameone.com/javadoc/com/codename1/ui/Component.html
438
Events
'Component' based events require focus
'Form' based events can block existing functionality from proceeding thru the event
chain e.g. you can avoid calling super in a form event and thus block other events
from happening (e.g. block a listener or component event from triggering).
Override Form
Override
Component
Form
Component
Block current
functionality
Partially (event
consume)
No
Notice that most pointer events have a version that accepts an array as an argument,
this allows for multi-touch event handling by sending all the currently touched
coordinates.
21
https://www.codenameone.com/javadoc/com/codename1/ui/TextField.html
439
Events
return DRAG_REGION_LIKELY_DRAG_XY;
This indicates that we want all drag events on both AXIS to be sent as soon as possible.
Notice that this doesnt completely disable event sanitation.
10.3.BrowserNavigationCallback
The BrowserNavigationCallback
classification for it.
22
https://www.codenameone.com/javadoc/com/codename1/ui/events/BrowserNavigationCallback.html
440
Events
You can use the browser navigation callback to change the UI or even to invoke Java
code from JavaScript code e.g.:
bc.setBrowserNavigationCallback((url) -> {
if(url.startsWith("http://click")) {
return false;
return true;
441
11.1.Jar Resources
Resources that are packaged within the "JAR" dont really belong in this
section of the developer guide but since they are often confused with
Storage / FileSystemStorage this might be the best place to clarify what they are.
You can place arbitrary files within the src directory of a Codename One project. This
file will get packaged into the final distribution. In standard Java SE you can usually
do something like:
InputStream i = getClass().getResourceAsStream("/myFile");
This isnt guaranteed to work on all platforms and will probably fail on some. Instead
you should use something such as:
InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/
myFile");
This isnt the only limitation though, you can use hierarchies so something like this
would fail:
InputStream i = Display.getInstance().getResourceAsStream(getClass(), "/
res/myFile");
You cant use relative paths either so this will fail as well (notice the lack of the first
slash):
InputStream i =
Display.getInstance().getResourceAsStream(getClass(), "myFile");
The reason for those limitations is portability, on iOS and Android resources behave
quite differently so supporting the full Java SE semantics is unrealistic.
442
11.2.Storage
1
Storage is accessed via the Storage class. It is a flat filesystem like interface and
contains the ability to list/delete and write to named storage entries.
The Storage API also provides convenient methods to write objects to Storage
and read them from Storage specifically readObject & writeObject .
The object in Storage are deleted when an app is uninstalled but
are retained between application updates.
The sample code below demonstrates listing the content of the storage, adding/viewing
and deleting entries within the storage:
Toolbar.setGlobalToolbar(true);
try(OutputStream os =
Storage.getInstance().createOutputStream(tf.getText())) {
os.write(body.getText().getBytes("UTF-8"));
createFileEntry(hi, tf.getText());
hi.getContentPane().animateLayout(250);
} catch(IOException err) {
}
1
Log.e(err);
https://www.codenameone.com/javadoc/com/codename1/io/Storage.html
443
}
hi.show();
FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE);
FontImage.setMaterialIcon(view, FontImage.MATERIAL_OPEN_IN_NEW);
Container content = BorderLayout.center(fileField);
int size = Storage.getInstance().entrySize(file);
try(InputStream is = Storage.getInstance().createInputStream(file))
String s = Util.readToString(is, "UTF-8");
Dialog.show(file, s, "OK", null);
} catch(IOException err) {
}
Log.e(err);
});
hi.add(content);
444
445
Storage also offers a very simple API in the form of the Preferences class. The
Preferences class allows developers to store simple variables, strings, numbers,
booleans etc. in storage without writing any storage code. This is a common use case
within applications e.g. you have a server token that you need to store you can store
it like this:
2
https://www.codenameone.com/javadoc/com/codename1/io/Preferences.html
446
Preferences.set("token", myToken);
11.3.File System
3
java.io.File
&
java.io.FileInputStream
werent
supported directly has a lot to do with the richness of those two APIs. They effectively
allow saving a file anywhere, however mobile devices are far more restrictive and dont
allow apps to see/modify files that are owned by other apps.
https://www.codenameone.com/javadoc/com/codename1/io/FileSystemStorage.html
447
FileSystemStorage
API can be a
if(parent == null) {
files = FileSystemStorage.getInstance().getRoots();
return new Vector<Object>(Arrays.asList(files));
} else {
try {
files =
FileSystemStorage.getInstance().listFiles((String)parent);
} catch(IOException err) {
Log.e(err);
}
}
String p = (String)parent;
448
}
}
result.add(p + s);
return result;
@Override
};
return !FileSystemStorage.getInstance().isDirectory((String)node);
return n;
return n.substring(pos);
};
hi.add(BorderLayout.CENTER, t);
hi.show();
449
Storage
File System
JAR Resource
Initial State
Blank
Blank
As defined by
developer
Modifiable
Yes
Yes
No
Supports
Hierarchies
No
Yes
No
11.4.SQL
Most new devices contain one version of sqlite or another; sqlite is a very lightweight
SQL database designed for embedding into devices. For portability we recommend
avoiding SQL altogether since it is both fragmented between devices (different sqlite
versions) and isnt supported on all devices.
In general SQL seems overly complex for most embedded device programming tasks.
451
Portability Of SQLite
SQLite is supported on iOS, Android, RIM, Desktop & JavaScript builds.
However, the JavaScript version of SQL has been deprecated and isnt
supported on all platforms.
You will notice that at this time support is still missing from the Windows builds.
The biggest issue with SQLite portability is in iOS. The SQLite version for most
platforms is threadsafe and as a result very stable. However, the iOS version
is not!
This might not seem like a big deal normally, however if you forget to close a
connection the GC might close it for you thus producing a crash. This is such a
common occurrence that Codename One logs a warning when the GC collects
a database resource on the simulator.
SQL is pretty powerful and very well suited for common tabular data. The Codename
One SQL API is similar in spirit to JDBC but considerably simpler since many of the
abstractions of JDBC designed for pluggable database architecture make no sense for
a local database.
4
The Database API is a high level abstraction that allows you to open an arbitrary
database file using syntax such as:
Database db = Display.getInstance().openOrCreate(databaseName);
Some SQLite apps ship with a "ready made" database. We allow you to replace the
DB file by using the code:
String path = Display.getInstance().getDatabasePath(databaseName);
You can then use the FileSystemStorage class to write the content of your DB file
into the path. Notice that it must be a valid SQLite file!
This is very useful for applications that need to synchronize with a central server or
applications that ship with a large database as part of their core product.
4
https://www.codenameone.com/javadoc/com/codename1/db/Database.html
452
Toolbar.setGlobalToolbar(true);
Style s = UIManager.getInstance().getComponentStyle("TitleCommand");
FontImage icon =
FontImage.createMaterial(FontImage.MATERIAL_QUERY_BUILDER, s);
db = Display.getInstance().openOrCreate("MyDB.db");
if(query.getText().startsWith("select")) {
cur = db.executeQuery(query.getText());
int columns = cur.getColumnCount();
hi.removeAll();
if(columns > 0) {
columnNames[iter] = cur.getColumnName(iter);
while(next) {
String[columns];
currentRow.getString(iter);
currentRowArray[iter] =
}
data.add(currentRowArray);
next = cur.next();
DefaultTableModel(columnNames, arr)));
453
results");
} else {
results");
} else {
db.execute(query.getText());
hi.add(BorderLayout.CENTER, "Query completed
successfully");
}
hi.revalidate();
} catch(IOException err) {
Log.e(err);
hi.removeAll();
} finally {
Util.cleanup(db);
Util.cleanup(cur);
});
hi.show();
454
455
http://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html
456
To open a connection one needs to use a ConnectionRequest object, which has some
similarities to the networking mechanism in JavaScript but is obviously somewhat more
elaborate.
You can send a get request to a URL using something like:
ConnectionRequest request = new ConnectionRequest(url, false);
request.addResponseListener((e) -> {
// process the response
});
NetworkManager.getInstance().addToQueue(request);
Notice that you can also implement the same thing and much more by
avoiding the response listener code and instead overriding the methods of the
ConnectionRequest class which offers multiple points to override e.g.
ConnectionRequest request = new ConnectionRequest(url, false) {
protected void readResponse(InputStream input) {
}
networking code
}
// to update the UI
// writes post data, by default this just works but if you want
to write this
}
};
NetworkManager.getInstance().addToQueue(request);
http://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html
457
NetworkManager.getInstance().addToQueueAndWait(request);
byte[] resultOfRequest = request.getData();
Notice that in this case the addToQueueAndWait method returned after the
connection completed. Also notice that this was totally legal to do on the EDT!
11.5.1.Threading
By default the NetworkManager launches with a single network thread. This is
sufficient for very simple applications that dont do too much networking but if you need
to fetch many images concurrently and perform web services in parallel this might be
an issue.
Once you increase the thread count there is no guarantee of order for
your requests. Requests might not execute in the order with which
you added them to the queue!
To update the number of threads use:
NetworkManager.getInstance().updateThreadCount(4);
All the callbacks in the ConnectionRequest occur on the network thread and not
on the EDT!
There is one exception to this rule which is the postResponse() method designed
to update the UI after the networking code completes.
7
458
Arguments
HTTP supports several "request methods", most commonly GET & POST but also a
few others such as HEAD , PUT , DELETE etc.
Arguments in HTTP are passed differently between GET and POST methods. That is
what the setPost method in Codename One determines, whether arguments added
to the request should be placed using the GET semantics or the POST semantics.
So if we continue our example from above we can do something like this:
ConnectionRequest request = new ConnectionRequest(url, false);
request.addArgument("MyArgName", value);
This will implicitly add a get argument with the content of value . Notice that we dont
really care what value is. Its implicitly HTTP encoded based on the get/post semantics.
In this case it will use the get encoding since we passed false to the constructor.
A simpler implementation could do something like this:
ConnectionRequest request = new ConnectionRequest(url +
"MyArgName=" + Util.encodeUrl(value), false);
This would be almost identical but doesnt provide the convenience for switching back
and forth between GET / POST and it isnt as fluent.
We can skip the encoding in complex cases where server code expects illegal HTTP
characters (this happens) using the addArgumentNoEncoding method. We can
also add multiple arguments with the same key using addArgumentArray .
459
Methods
As we explained above, the setPost() method allows us to manipulate the get/post
semantics of a request. This implicitly changes the POST or GET method submitted
to the server.
However, if you wish to have finer grained control over the submission process e.g. for
making a HEAD request you can do this with code like:
ConnectionRequest request = new ConnectionRequest(url, false);
request.setHttpMethod("HEAD");
Headers
When communicating with HTTP servers we often pass data within headers mostly for
authentication/authorization but also to convey various properties.
Some headers are builtin as direct APIs e.g. content type is directly exposed within
the API since its a pretty common use case. We can set the content type of a post
request using:
ConnectionRequest request = new ConnectionRequest(url, true);
request.setContentType("text/xml");
We can also add any arbitrary header type we want, e.g. a very common use case is
basic authorization where the authorization header includes the Base64 encoded user/
password combination as such:
String authCode = user + ":" + password;
String authHeader = "Basic " + Base64.encode(authCode.getBytes());
request.addRequestHeader("Authorization", authHeader);
This can be quite tedious to do if you want all requests from your app to use this header.
For this use case you can just use:
String authCode = user + ":" + password;
String authHeader = "Basic " + Base64.encode(authCode.getBytes());
NetworkManager.getInstance().addDefaultHeader("Authorization",
authHeader);
460
Server Headers
Server returned headers are a bit trickier to read. We need to subclass the connection
request and override the readHeaders method e.g.:
ConnectionRequest request = new ConnectionRequest(url, false) {
//....
}
};
NetworkManager.getInstance().addToQueue(request);
Here we can extract the headers one by one to handle complex headers such as
cookies, authentication etc.
Error Handling
As you noticed above practically all of the methods in the ConectionRequest throw
IOException . This allows you to avoid the try / catch semantics and just let the
error propagate up the chain so it can be handled uniformly by the application.
There are two distinct placed where you can handle a networking error:
The ConnectionRequest - by overriding callback methods
The NetworkManager error handler
Notice that the NetworkManager error handler takes precedence thus allowing you
to define a global policy for network error handling by consuming errors.
E.g. if I would like to block all network errors from showing anything to the user I could
do something like this:
NetworkManager.getInstance().addToQueue(request);
NetworkManager.getInstance().addErrorListener((e) -> e.consume());
461
The error listener is invoked first with the NetworkEvent matching the error.
Consuming the event prevents it from propagating further down the chain into the
ConnectionRequest callbacks.
We can also override the error callbacks of the various types in the request e.g. in the
case of a server error code we can do:
ConnectionRequest request = new ConnectionRequest(url, false) {
// do something
}
};
NetworkManager.getInstance().addToQueue(request);
NetworkManager.getInstance().addToQueueAndWait(request);
if(request.getResponseCode() != 200) {
}
// probably an error...
This code will only work with the synchronous "AndWait" version of
the method since the response code will take a while to return for
the non-wait version.
8
https://www.codenameone.com/javadoc/com/codename1/io/NetworkEvent.html
462
Error Stream
When we get an error code that isnt 200/300 we ignore the result. This is problematic
as the result might contain information we need. E.g. many webservices provide further
XML/JSON based details describing the reason for the error code.
Calling setReadResponseForErrors(true) will trigger a mode where even
errors will receive the readResponse callback with the error stream. This also means
that APIs like getData and the listener APIs will also work correctly in case of error.
11.5.3.GZIP
Gzip is a very common compression format based on the lz algorithm, its used by web
servers around the world to compress data.
9
10
Do the rest as usual and you should have smaller responses from the servers.
https://www.codenameone.com/javadoc/com/codename1/io/gzip/GZIPInputStream.html
10
https://www.codenameone.com/javadoc/com/codename1/io/gzip/GZIPOutputStream.html
11
https://www.codenameone.com/javadoc/com/codename1/io/gzip/GZConnectionRequest.html
463
11.5.4.File Upload
MultipartRequest
12
tries to simplify the process of uploading a file from the local device
to a remote server.
You can always submit data in the buildRequestBody but this is flaky and has some
limitations in terms of devices/size allowed. HTTP standardized file upload capabilities
thru the multipart request protocol, this is implemented by countless servers and is well
documented. Codename One supports this out of the box:
MultipartRequest request = new MultipartRequest();
request.setUrl(url);
request.addData("myFileName", fullPathToFile, "text/plain")
NetworkManager.getInstance().addToQueue(request);
// 100 MB
@Override
try(InputStream is = data.getInputStream()) {}
12
https://www.codenameone.com/javadoc/com/codename1/io/MultipartRequest.html
464
11.5.5.Parsing
Codename One has several built in parsers for JSON, XML, CSV & Properties formats.
You can use those parsers to read data from the Internet or data that is shipping with
your product. E.g. use the CSV data to setup default values for your application.
All our parsers are designed with simplicity and small distribution size; they dont
validate and will fail in odd ways when faced with broken data. The main logic behind
this is that validation takes up CPU time on the device where CPU is a precious
resource.
Parsing CSV
CSV is probably the easiest to use, the "Comma Separated Values" format is just a list
of values separated by commas (or some other character) with new lines to indicate
another row in the table. These usually map well to an Excel spreadsheet or database
table and are supported by default in all spreadsheets.
To parse a CSV just use the CSVParser
13
class as such:
} catch(IOException err) {
Log.e(err);
}
hi.show();
13
https://www.codenameone.com/javadoc/com/codename1/io/CSVParser.html
465
that
we
used
CharArrayReader
from
the
JSON
The JSON ("Java Script Object Notation") format is popular on the web for passing
values to/from webservices since it works so well with JavaScript. Parsing JSON is just
14
as easy but has two different variations. You can use the JSONParser class to build
a tree of the JSON data as such:
JSONParser parser = new JSONParser();
The sample below uses results from an API of ice and fire that queries structured data
about the "Song Of Ice & Fire" book series. Here is a sample result returned from the API
for the query http://www.anapioficeandfire.com/api/characters?page=5&pageSize=3 :
14
15
https://www.codenameone.com/javadoc/com/codename1/io/JSONParser.html
https://anapioficeandfire.com/
466
"url": "http://www.anapioficeandfire.com/api/characters/13",
"name": "Chayle",
"culture": "",
"born": "",
"died": "In 299 AC, at Winterfell",
"titles": [
"Septon"
],
"aliases": [],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"http://www.anapioficeandfire.com/api/books/1",
"http://www.anapioficeandfire.com/api/books/2",
"http://www.anapioficeandfire.com/api/books/3"
],
"povBooks": [],
"tvSeries": [],
"playedBy": []
},
{
"url": "http://www.anapioficeandfire.com/api/characters/14",
"name": "Gillam",
"culture": "",
"born": "",
"died": "",
"titles": [
"Brother"
],
"aliases": [],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"http://www.anapioficeandfire.com/api/books/5"
],
"povBooks": [],
"tvSeries": [],
"playedBy": []
},
467
"url": "http://www.anapioficeandfire.com/api/characters/15",
"name": "High Septon",
"culture": "",
"born": "",
"died": "",
"titles": [
"High Septon",
"His High Holiness",
"Father of the Faithful",
"Voice of the Seven on Earth"
],
"aliases": [
"The High Sparrow"
],
"father": "",
"mother": "",
"spouse": "",
"allegiances": [],
"books": [
"http://www.anapioficeandfire.com/api/books/5",
"http://www.anapioficeandfire.com/api/books/8"
],
"povBooks": [],
"tvSeries": [
"Season 5"
],
"playedBy": [
"Jonathan Pryce"
]
We will place that into a file named "anapioficeandfire.json" in the src directory to make
the next sample simpler:
Form hi = new Form("JSON Parsing", new BoxLayout(BoxLayout.Y_AXIS));
JSONParser json = new JSONParser();
try(Reader r = new
InputStreamReader(Display.getInstance().getResourceAsStream(getClass(), "/
anapioficeandfire.json"), "UTF-8")) {
Map<String, Object> data = json.parseJSON(r);
java.util.List<Map<String, Object>> content =
(java.util.List<Map<String, Object>>)data.get("root");
for(Map<String, Object> obj : content) {
468
java.util.List<String> aliases =
(java.util.List<String>)obj.get("aliases");
name = aliases.get(0);
mb.setTextLine2(titles.get(0));
}
mb.addActionListener((e) -> Display.getInstance().execute(url));
hi.add(mb);
} catch(IOException err) {
Log.e(err);
}
hi.show();
The JSONParser returns a Map which is great if the root object is a Map but
in some cases its a list of elements (as is the case above). In this case a special
case "root" element is created to contain the actual list of elements.
We rely that the entries are all maps, this might not be the case for every API type.
Notice that the "titles" and "aliases" entries are both lists of elements. We use
java.util.List to avoid a clash with com.codename1.ui.List .
469
Figure 11.7. Parsed JSON result, clicking the elements opens the
URL from the JSON
The structure of the returned map is sometimes unintuitive when
looking at the raw JSON. The easiest thing to do is set a breakpoint
on the method and use the inspect variables capability of your IDE
to inspect the returned element hierarchy while writing the code to
extract that data
An alternative approach is to use the static data parse() method of the JSONParser
class and implement a callback parser e.g.:
470
methods are invoked by the parser to indicate internal parser states, this is similar to
the way traditional XML SAX event parsers work.
XML Parsing
16
The XMLParser started its life as an HTML parser built for displaying mobile HTML.
That usage has since been deprecated but the parser can still parse many HTML pages
and is very "loose" in terms of verification. This is both good and bad as the parser will
work with invalid data without complaining.
The simplest usage of XMLParser looks a bit like this:
XMLParser parser = new XMLParser();
The Element contains children and attributes. It represents a tag within the XML
document and even the root document itself. You can iterate over the XML tree to
extract the data from within the XML file.
Weve had a great sample of working with XMLParser in the Tree Section of this
guide.
18
XMLParser has the complimentary XMLWriter class which can generate XML from
the Element hierarchy. This allows a developers to mutate (modify) the elements and
save them to a writer stream.
XPath Processing
19
20
The Result class provides a subset of XPath , but it is not limited to just XML
documents, it can also work with JSON documents, and even with raw Map objects.
16
https://www.codenameone.com/javadoc/com/codename1/xml/XMLParser.html
17
https://www.codenameone.com/javadoc/com/codename1/xml/Element.html
18
https://www.codenameone.com/javadoc/com/codename1/xml/XMLWriter.html
19
https://www.codenameone.com/javadoc/com/codename1/processing/Result.html
20
http://www.w3schools.com/xsl/xpath_intro.asp
471
<address_component>
<long_name>Ontario</long_name>
<short_name>ON</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Canada</long_name>
<short_name>CA</short_name>
<type>country</type>
<type>political</type>
</address_component>
</result>
</GeocodeResponse>
We want to extract some of the data above into simpler string results. We can do this
using:
Result result = Result.fromContent(input, Result.XML);
String country = result.getAsString("/result/
address_component[type='country']/long_name");
String region = result.getAsString("/result/
address_component[type='administrative_area_level_1']/long_name");
String city = result.getAsString("/result/
address_component[type='locality']/long_name");
21
https://developers.google.com/maps/documentation/geocoding/
472
from
22
23
24
To use the expression processor when calling a webservice, you could use something
like the following to parse JSON (notice this is interchangeable between JSON and
XML):
Form hi = new Form("Location", new BoxLayout(BoxLayout.Y_AXIS));
hi.add("Pinpointing Location");
Display.getInstance().callSerially(() -> {
Location l =
Display.getInstance().getLocationManager().getCurrentLocationSync();
ConnectionRequest request = new ConnectionRequest("http://
maps.googleapis.com/maps/api/geocode/json", false) {
private String country;
private String region;
private String city;
private String json;
@Override
{
https://www.codenameone.com/javadoc/com/codename1/xml/Element.html
https://www.codenameone.com/javadoc/com/codename1/xml/XMLParser.html
https://www.codenameone.com/javadoc/com/codename1/io/JSONParser.html
473
hi.add(new SpanLabel(json));
}
hi.revalidate();
};
request.setContentType("application/json");
request.addRequestHeader("Accept", "application/json");
request.addArgument("sensor", "true");
request.addArgument("latlng", l.getLatitude() + "," +
l.getLongitude());
NetworkManager.getInstance().addToQueue(request);
});
hi.show();
[source,java]
The returned JSON looks something like this (notice its snipped because the data is
too long):
{
"status": "OK",
"results": [
{
"place_id": "ChIJJ5T9-iFawokRTPGaOginEO4",
"formatted_address": "280 Broadway, New York, NY 10007, USA",
"address_components": [
{
"short_name": "280",
"types": ["street_number"],
"long_name": "280"
},
{
"short_name": "Broadway",
"types": ["route"],
"long_name": "Broadway"
},
{
"short_name": "Lower Manhattan",
474
"neighborhood",
"political"
],
},
{
"short_name": "Manhattan",
"types": [
"sublocality_level_1",
"sublocality",
"political"
],
"long_name": "Manhattan"
},
{
},
{
},
{
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
],
"long_name": "New York"
},
{
"short_name": "US",
"types": [
"country",
"political"
],
"long_name": "United States"
475
"short_name": "10007",
"types": ["postal_code"],
"long_name": "10007"
},
{
"short_name": "1868",
"types": ["postal_code_suffix"],
"long_name": "1868"
],
"types": ["street_address"],
"geometry": {
"viewport": {
"northeast": {
"lng": -74.0044642197085,
"lat": 40.7156470802915
},
"southwest": {
"lng": -74.0071621802915,
"lat": 40.7129491197085
}
},
"location_type": "ROOFTOP",
"location": {
"lng": -74.00581319999999,
"lat": 40.7142981
}
476
477
Second Example
It also possible to do some more complex expressions. Well use the following XML
fragment for the next batch of examples:
<rankings type="aus" gender="male" date="2011-12-31">
</rankings>
Above, if you want to select the IDs of all players that are ranked in the top 2, you can
use an expression like:
int top2[] = result.getAsIntegerArray("//player[@rank < 3]/@id");
478
It is also possible to select parent nodes, by using the .. expression. For example:
int id = result.getAsInteger("//lastname[text()='Hewitt']/../@id");
Above, we globally find a lastname element with a value of Hewitt, then grab the parent
node of lastname which happens to be the player node, then grab the id attribute from
the player node. Alternatively, you could get the same result from the following simpler
statement:
int id = result.getAsInteger("//player[lastname='Hewitt']/@id");
In the above example, if the player node had an address object, wed be selecting all
players from Canada. This is a simple example of a nested expression, but they can
get much more complex, which will be required as the documents themselves get more
complex.
Moving on, to select a node based on the existence of an attribute:
int id[] = result.getAsIntegerArray("//player[@rank]/@id");
Above, we selected the IDs of all ranked players. Conversely, we can select the nonranked players like this:
479
Above, we selected all players that have a middle name.<br/> Keep in mind that the
Codename One path expression language is not a full implementation of XPath 1.0,
but does already handle many of the most useful features of the specification.
Properties Files
25
Properties files are standard key/value pairs encoded into a text file. This file format
is very familiar to Java developers and the Codename One specific version tries to be
as close as possible to the original Java implementation.
Notice that properties file both in Java proper and in Codename One dont support nonascii characters. In order to encode unicode values into the properties file format you
should use the native2ascii tool that ships with the JDK.
25
https://www.codenameone.com/javadoc/com/codename1/io/Properties.html
480
11.6.1.Simpler Downloads
A very common task is file download to storage or filesystem.
The Util
26
downloadUrlToFileSystemInBackground ,
downloadUrlToStorageInBackground,
downloadUrlToStorage .
26
downloadUrlToFile
https://www.codenameone.com/javadoc/com/codename1/io/Util.html
481
&
27
ConnectionRequest.setDestinationStorage(fileName) / ConnectionRequest.setDe
Both of which simplify the whole process of downloading a file.
Downloading Images
Codename One has multiple ways to download an image and the general
28
recommendation is the URLImage . However, the URLImage assumes that you
know the size of the image in advance or that you are willing to resize it. In that regard
it works great for some use cases but not so much for others.
The download methods mentioned above are great alternatives but they are a bit
verbose when working with images and dont provide fine grained control over the
ConnectionRequest e.g. making a POST request to get an image.
Adding global headers is another use case but you can use
29
addDefaultHeader to add those.
To make this process simpler there is a set of helper methods in ConnectionRequest
30
that downloads images directly .
These methods complement the Util methods but go a bit further and feature very
terse syntax e.g. you can just download a ConnectionRequest to Storage using
code like this:
request.downloadImageToStorage(url, (img) ->
theImageIsHereDoSomethingWithIt(img));
31
for
https://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html
https://www.codenameone.com/javadoc/com/codename1/ui/URLImage.html
https://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html#addDefaultHeaderjava.lang.String-java.lang.String30
https://www.codenameone.com/javadoc/com/codename1/io/
ConnectionRequest.html#downloadImageToStorage-java.lang.Stringcom.codename1.util.SuccessCallback31
https://www.codenameone.com/javadoc/com/codename1/util/SuccessCallback.html
482
11.7.Webservice Wizard
The Webservice Wizard can be invoked directly from the plugin. It generates stubs for
the client side that allow performing simple method invocations on the server. It also
generates a servlet that can be installed on any servlet container to intercept client
side calls.
There are limits to the types of values that can be passed via the webservice wizard
protocol but it is highly efficient since it is a binary protocol and very extensible
thru object externalization. All methods are provided both as asynchronous and
synchronous calls for the convenience of the developer.
483
485
Figure 11.14. In the main Codename One project right click and
select the WebService Wizard option
486
Figure 11.15. Set the package and class name for the webservice
abstraction (notice this isnt your main class name) and then add
the methods you want in the webservice
487
488
Figure 11.17. Pick the directory in the server project to which the
source files will be generated by default this is the src/java directory
under the project we created in the first step
Figure 11.18. If you saved to the right location the server project
directory should look like this
489
return null;
return null;
All we need to do is fill in the code, for this example well only implement the first method
for simplicity:
public class GameOfThronesServiceServer {
return null;
Now lets open the client side code, in the GameOfThronesService.java file we
see this
public class GameOfThronesService {
cn1proxy";
//...
490
The destination URL needs to point at the actual server which you will recall from the
new project creation should include "HelloWebServiceWizard". So we can fix the URL
to:
private static final String DESTINATION_URL = "http://localhost:8080/
HelloWebServiceWizard/cn1proxy";
You would naturally need to update the host name of the server for running on a device
otherwise the device would need to reside within your internal network and point to
your IP address.
It is now time to write the actual client code that calls this. Every method we defined
above is now defined as a static method within the GameOfThronesService class
with two permutations. One is a synchronous permutation that behaves exactly as
expected. It blocks the calling thread while calling the server and might throw an
IOException if something failed.
This type of method (synchronous method) is very easy to work with since its
completely legal to call it from the event dispatch thread and its very easy to map it
to application logic flow.
The second type of method uses the async JavaScript style callbacks and accepts the
callback interface. It returns immediately and doesnt throw any exception. It will call
onSuccess/onError based on the server result.
You can pick either one of these approaches based on your personal preferences. Here
we demonstrate both uses with the server API:
Form hi = new Form("WebService Wizard", new BoxLayout(BoxLayout.Y_AXIS));
Button getNamesSync = new Button("Get Names - Sync");
hi.add(b);
491
} catch(IOException err) {
});
Log.e(err);
getNamesASync.addActionListener((e) -> {
GameOfThronesService.getBookNamesAsync(new Callback<String[]>() {
@Override
for(String b : value) {
hi.add(b);
}
hi.revalidate();
@Override
String errorMessage) {
Log.e(err);
}
});
});
492
The CachedDataService allows caching data and only updating it if the data changed
on the server. Normally the download APIs wont check for update if there is a local
cache of the data (e.g. URLImage always uses the local copy). This isnt a bad thing,
its pretty efficient.
32
https://www.codenameone.com/javadoc/com/codename1/io/services/CachedDataService.html
493
d = new CachedData();
d.setUrl("http://....");
});
Storage.getInstance().writeObject("LocallyCachedData", d);
11.9.Externalizable Objects
33
Codename One provides the Externalizable interface, which is similar to the Java
SE Externalizable interface. This interface allows an object to declare itself as
Externalizable for serialization (so an object can be stored in a file/storage or sent
over the network). However, due to the lack of reflection and use of obfuscation these
34
objects must be registered with the Util class.
Codename One doesnt support the Java SE Serialization API due to the size issues
and complexities related to obfuscation.
The major objects that are supported by default in the Codename One
Externalizable are: String , Collection , Map , ArrayList , HashMap ,
Vector , Hashtable , Integer , Double , Float , Byte , Short , Long ,
Character , Boolean , Object[] , byte[] , int[] , float[] , long[] ,
double[] .
33
34
https://www.codenameone.com/javadoc/com/codename1/io/Externalizable.html
https://www.codenameone.com/javadoc/com/codename1/io/Util.html
494
Storage.getInstance().writeObject("Test", h);
However, notice that some things arent polymorphic e.g. if we will externalize a
String[] we will get back an Object[] since String arrays arent detected by
the implementation.
The externalization process caches objects so the app will seem to
work and only fail on restart!
Implementing the Externalizable interface is only important when we want
to store a proprietary object. In this case we must register the object with the
com.codename1.io.Util class so the externalization algorithm will be able to
recognize it by name by invoking:
Util.register("MyClass", MyClass.class);
The getVersion() method returns the current version of the object allowing the
stored data to change its structure in the future (the version is then passed when
internalizing the object). The object id is a String uniquely representing the object; it
usually corresponds to the class name (in the example above the Unique Name should
be MyClass ).
495
out.writeBoolean(true);
out.writeUTF(value);
} else {
}
out.writeBoolean(false);
if(domain != null) {
out.writeBoolean(true);
out.writeUTF(domain);
} else {
out.writeBoolean(false);
}
out.writeLong(expires);
if(in.readBoolean()) {
}
value = in.readUTF();
if(in.readBoolean()) {
domain = in.readUTF();
}
expires = in.readLong();
496
Util.writeUTF(name, out);
Util.writeUTF(value, out);
Util.writeUTF(domain, out);
out.writeLong(expires);
Assuming we added a new date field to the object we can do the following. Notice that
a Date is really a long value in Java that can be null. For completeness the full
class is presented below:
public class MyClass implements Externalizable {
private static final int VERSION = 2;
private String name;
return VERSION;
return "MyClass";
out.writeBoolean(true);
out.writeLong(date.getTime());
497
out.writeBoolean(false);
}
out.writeLong(expires);
IOException {
name = Util.readUTF(in);
value = Util.readUTF(in);
domain = Util.readUTF(in);
if(version > 1) {
}
expires = in.readLong();
Notice that we only need to check for compatibility during the reading process as the
writing process always writes the latest version of the data.
The process of showing a progress bar for a long IO operation such as downloading
36
is automatically mapped to the IO stream in Codename One using the SliderBridge
class.
You can simulate network delays and disconnected network in the
Simulator menu
35
36
https://www.codenameone.com/javadoc/com/codename1/components/InfiniteProgress.html
https://www.codenameone.com/javadoc/com/codename1/components/SliderBridge.html
498
www.codenameone.com/img/blog/new_icon.png", false);
SliderBridge.bindProgress(cr, progress);
NetworkManager.getInstance().addToQueueAndWait(cr);
if(cr.getResponseCode() == 200) {
hi.add(BorderLayout.CENTER, new
ScaleImageLabel(EncodedImage.create(cr.getResponseData())));
hi.revalidate();
}
});
hi.add(BorderLayout.SOUTH, progress).add(BorderLayout.NORTH, download);
hi.show();
499
https://www.codenameone.com/javadoc/com/codename1/io/Log.html
500
This code will send a log every 2 minutes to your email if anything was changed. You
can place it within the init(Object) method of your application.
For a production application you can use Log.REPORTING_PRODUCTION which will
only email the log on exception.
11.12.Sockets
At this moment Codename One only supports TCP sockets. Server socket (listen/
accept) is only available on Android and the simulator but not on iOS.
You can check if Sockets are supported using the Socket.isSupported() . You
can test for server socket support using Socket.isServerSocketSupported() .
To use sockets you can use the Socket.connect(String host, int port,
SocketConnection 38 eventCallback) method.
To listen on sockets you can use the Socket.listen(int port, Class
scClass) method which will instantiate a SocketConnection instance (scClass
is expected to be a subclass of SocketConnection ) for every incoming connection.
This simple example allows you to create a server and a client assuming the device
supports both:
public class MyApplication {
private Form current;
UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()
[0]));
} catch(IOException e){
e.printStackTrace();
38
https://www.codenameone.com/javadoc/com/codename1/io/SocketConnection.html
501
if(current != null){
current.show();
return;
Socket.listen(5557, SocketListenerCallback.class);
});
connect.addActionListener((evt) -> {
System.out.println("Error");
@Override
OutputStream os) {
try {
int counter = 1;
while(isConnected()) {
} catch(Exception err) {
});
});
err.printStackTrace();
soc.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
soc.addComponent(btn);
soc.addComponent(connect);
soc.addComponent(host);
soc.show();
502
System.out.println("Error");
Display.getInstance().callSerially(new Runnable() {
public void run() {
if(connectionLabel == null) {
Display.getInstance().getCurrent().addComponent(connectionLabel);
} else {
connectionLabel.setText(t);
});
}
Display.getInstance().getCurrent().revalidate();
@Override
{
if(size > 0) {
}
} else {
Thread.sleep(50);
} catch(Exception err) {
err.printStackTrace();
503
current = Display.getInstance().getCurrent();
504
Chapter12.Miscellaneous Features
This chapter covers various features of Codename One that dont quite fit in any of the
other chapters :icons: font
12.1.Phone Functions
1
Most of the low level phone functionality is accessible in the Display class. Think of it
as a global central class covering your access to the "system".
12.1.1.SMS
Codename One supports sending SMS messages but not receiving them as this
functionality isnt portable. You can send an SMS using:
Display.getInstance().setSMS("+999999999", "My SMS Message");
Android/Blackberry support sending SMSs in the background without showing the user
anything. iOS & Windows Phone just dont have that ability, the best they can offer is
to launch the native SMS app with your message already in that app. Android supports
that capability as well (launching the OS native SMS app).
The default sendSMS API ignores that difference and simply works interactively on
iOS/Windows Phone while sending in the background for the other platforms.
The getSMSSupport API returns one of the following options:
SMS_NOT_SUPPORTED - for desktop, tablet etc.
SMS_SEAMLESS - sendSMS will not show a UI and will just send in the
background
SMS_INTERACTIVE - sendSMS will show an SMS sending UI
SMS_BOTH - sendSMS can support both seamless and interactive mode, this
currently only works on Android
The sendSMS can accept an interactive argument: sendSMS(String
phoneNumber, String message, boolean interactive)
1
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
505
Miscellaneous Features
The last argument will be ignored unless SMS_BOTH is returned from
getSMSSupport at which point you would be able to choose one way or the other.
The default behavior (when not using that flag) is the background sending which is the
current behavior on Android.
A typical use of this API would be something like this:
switch(Display.getInstance().getSMSSupport()) {
case Display.SMS_NOT_SUPPORTED:
return;
case Display.SMS_SEAMLESS:
showUIDialogToEditMessageData();
Display.getInstance().sendSMS(phone, data);
return;
default:
Display.getInstance().sendSMS(phone, data);
return;
12.1.2.Dialing
Dialog the phone is pretty trivial, this should open the dialer UI without physically dialing
the phone as that is discouraged by device vendors.
You can dial the phone by using:
Display.getInstance().dial("+999999999");
12.1.3.E-Mail
You can send an email via the platforms native email client with code such as this:
Message m = new Message("Body of message");
Display.getInstance().sendMessage(new String[]
You
can
add
one
attachment
setAttachmentMimeType .
by
using
setAttachment
and
506
Miscellaneous Features
You can add more than one attachment by putting them directly into the attachment
map e.g.:
Message m = new Message("Body of message");
m.getAttachments().put(textAttachmentUri, "text/plain");
m.getAttachments().put(imageAttachmentUri, "image/png");
Display.getInstance().sendMessage(new String[]
The email messaging API has an additional ability within the Message class. The
sendMessageViaCloud method allows you to use the Codename One cloud to send
an email without end user interaction. This feature is available to pro users only since
it makes use of the Codename One cloud:
Message m = new Message("<html><body>Check out <a href=\"https://
www.codenameone.com/\">Codename One</a></body></html>");
m.setMimeType(Message.MIME_HTML);
12.2.Contacts API
The contacts API provides us with the means to query the phones address
book, delete elements from it and create new entries into it. To get the
platform specific list of contacts you can use String[]
contacts
=
ContactsManager.getAllContacts();
Notice that on some platforms this will prompt the user for permissions and the user
might choose not to grant that permission. To detect whether this is the case you can
invoke isContactsPermissionGranted() after invoking getAllContacts() .
This can help you adapt your error message to the user.
2
https://www.codenameone.com/javadoc/com/codename1/messaging/Message.html
507
Miscellaneous Features
3
Once you have a Contact you can use the getContactById method, however the
default method is a bit slow if you want to pull a large batch of contacts. The solution
for this is to only extract the data that you need via
getContactById(String id, boolean includesFullName,
includesEmail,
boolean includeAddress)
Here you can specify true only for the attributes that actually matter to you.
Another capability of the contacts API is the ability to extract all of the contacts very
quickly. This isnt supported on all platforms but platforms such as Android can really
get a boost from this API as extracting the contacts one by one is remarkably slow on
Android.
You can check if a platform supports the extraction of all the contacts quickly thru
ContactsManager.isGetAllContactsFast() .
When retrieving all the contacts, notice that you should probably not
retrieve all the data and should set some fields to false to perform
a more efficient query
You can then extract all the contacts using code that looks a bit like this, notice that we
use a thread so the UI wont be blocked!
Form hi = new Form("Contacts", new BoxLayout(BoxLayout.Y_AXIS));
hi.add(new InfiniteProgress());
Display.getInstance().scheduleBackgroundTask(() -> {
Contact[] contacts = ContactsManager.getAllContacts(true, true, false,
true, false, false);
Display.getInstance().callSerially(() -> {
hi.removeAll();
for(Contact c : contacts) {
});
3
}
hi.getContentPane().animateLayout(150);
https://www.codenameone.com/javadoc/com/codename1/contacts/Contact.html
508
Miscellaneous Features
});
hi.show();
Miscellaneous Features
mb.putClientProperty("id", c.getId());
Display.getInstance().scheduleBackgroundTask(() -> {
Contact cc = ContactsManager.getContactById(c.getId(),
false, true, false, false, false);
Display.getInstance().callSerially(() -> {
Image photo = cc.getPhoto();
if(photo != null) {
});
});
});
});
mb.setIcon(photo.fill(size, size));
mb.revalidate();
}
hi.getContentPane().animateLayout(150);
510
Miscellaneous Features
511
Miscellaneous Features
Miscellaneous Features
be automatically localized. You can just use the Property Key used in the localization
bundle in the Command name of the form.
You can add additional languages by pressing the Add Locale button.
This generates bundles in the resource file which are really just key/value pairs
mapping a string in one language to another language. You can install the bundle using
code like this:
UIManager.getInstance().setBundle(res.getL10N("l10n", local));
The device language (as an ISO 639 two letter code) could be retrieved with this:
String local = L10NManager.getInstance().getLanguage();
Once installed a resource bundle takes over the UI and every string set to a label (and
label like components) will be automatically localized based on the bundle. You can
4
also use the localize method of UIManager to perform localization on your own:
UIManager.getInstance().localize( "KeyInBundle", "DefaultValue");
The list of available languages in the resource bundle could be retrieved like this. Notice
that this a list that was set by you and doesnt need to confirm to the ISO language
code standards:
Resources res = fetchResourceFile();
Enumeration locales = res.listL10NLocales( "l10n" );
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html
513
Miscellaneous Features
is the one used by Androids string bundles which means most
localization specialists should easily localize it
The resource bundle is just a map between keys and values e.g. the code below
displays "This Label is localized" on the Label with the hardcoded resource
bundle. It would work the same with a resource bundle loaded from a resource file:
Form hi = new Form("L10N", new BoxLayout(BoxLayout.Y_AXIS));
12.3.1.Localization Manager
The L10NManager
localization tasks.
It allows formatting numbers/dates & time based on platform locale. It also provides
a great deal of the information you need such as the language/locale information you
need to pick the proper resource bundle.
Form hi = new Form("L10N", new TableLayout(16, 2));
add("formatDateLongStyle").add(l10n.formatDateLongStyle(new Date())).
add("formatDateShortStyle").add(l10n.formatDateShortStyle(new
Date())).
add("formatDateTime").add(l10n.formatDateTime(new Date())).
https://www.codenameone.com/javadoc/com/codename1/l10n/L10NManager.html
514
Miscellaneous Features
add("formatDateTimeMedium").add(l10n.formatDateTimeMedium(new
Date())).
add("formatDateTimeShort").add(l10n.formatDateTimeShort(new Date())).
add("getCurrencySymbol").add(l10n.getCurrencySymbol()).
add("getLanguage").add(l10n.getLanguage()).
add("getLocale").add(l10n.getLocale()).
add("isRTLLocale").add("" + l10n.isRTLLocale()).
add("parseCurrency").add(l10n.formatCurrency(l10n.parseCurrency("33.77$"))).
add("parseDouble").add(l10n.format(l10n.parseDouble("34.35"))).
add("parseInt").add(l10n.format(l10n.parseInt("56"))).
add("parseLong").add("" + l10n.parseLong("4444444"));
hi.show();
515
Miscellaneous Features
12.3.2.RTL/Bidi
RTL stands for right to left, in the world of internationalization it refers to languages that
are written from right to left (Arabic, Hebrew, Syriac, Thaana).
Most western languages are written from left to right (LTR), however some languages
are written from right to left (RTL) speakers of these languages expect the UI to flow
in the opposite direction otherwise it seems weird just like reading this word would be
to most English speakers: "drieW".
516
Miscellaneous Features
The problem posed by RTL languages is known as BiDi (Bi-directional) and not as RTL
since the "true" problem isnt the reversal of the writing/UI but rather the mixing of RTL
and LTR together. E.g. numbers are always written from left to right (just like in English)
so in an RTL language the direction is from right to left and once we reach a number
or English text embedded in the middle of the sentence (such as a name) the direction
switches for a duration and is later restored.
The main issue in the Codename One world is in the layouts, which need to reverse on
the fly. Codename One supports this via an RTL flag on all components that is derived
6
from the global RTL flag in UIManager .
Resource bundles can also include special case constant @rtl, which indicates if a
language is written from right to left. This allows everything to automatically reverse.
When in RTL mode the UI will be the exact mirror so WEST will become EAST ,
RIGHT will become LEFT and this would be true for paddings/margins as well.
If you have a special case where you dont want this behavior you will need to wrap
it with an isRTL check. You can also use setRTL on a per Component basis to
disable RTL behavior for a specific Component .
Most UI APIs have special cases for BiDi instead of applying it
globally e.g. AWT introduced constants such as LEADING instead
of making WEST mean the opposite direction. We think that was a
mistake since the cases where you wouldnt want the behavior of
automatic reversal are quite rare.
Codename Ones support for bidi includes the following components:
Bidi algorithm - allows converting between logical to visual representation for
rendering
Global RTL flag - default flag for the entire application indicating the UI should flow
from right to left
Individual RTL flag - flag indicating that the specific component/container should
be presented as an RTL/LTR component (e.g. for displaying English elements within
a RTL UI).
RTL text field input
6
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/UIManager.html
517
Miscellaneous Features
7
Most of Codename Ones RTL support is under the hood, the LookAndFeel global
RTL flag can be enabled using:
UIManager.getInstance().getLookAndFeel().setRTL(true);
Once RTL is activated all positions in Codename One become reversed and the UI
becomes a mirror of itself. E.g. Adding a Toolbar command to the left will actually
make it appear on the right. Padding on the left becomes padding on the right. The
scroll moves to the left etc.
This applies to the layout managers (except for group layout) and most components.
Bidi is mostly seamless in Codename One but a developer still needs to be aware that
his UI might be mirrored for these cases.
12.4.Location - GPS
8
The Location API allows us to track changes in device location or the current user
position.
The Simulator includes a Location Simulation tool that you can
launch to determine the current position of the simulator and debug
location events
The most basic usage for the API allows us to just fetch a device Location, notice that
this API is blocking and can take a while to return:
Location position =
LocationManager.getLocationManager().getCurrentLocationSync();
In order for location to work on iOS you MUST define the build
hint ios.locationUsageDescription and describe why your
application needs access to location. Otherwise you wont get
location updates!
The getCurrentLocationSync() method is very good for cases where you only
need to fetch a current location once and not repeatedly query location. It activates
the GPS then turns it off to avoid excessive battery usage. However, if an application
needs to track motion or position over time it should use the location listener API to
track location as such:
7
8
https://www.codenameone.com/javadoc/com/codename1/ui/plaf/LookAndFeel.html
https://www.codenameone.com/javadoc/com/codename1/location/Location.html
518
Miscellaneous Features
Notice that there is a method called getCurrentLocation()
which will return the current state immediately and might not be
accurate for some cases.
public MyListener implements LocationListener {
// update UI etc.
LocationManager.getLocationManager().setLocationListener(new
MyListener());
On Android location maps to low level APIs if you disable the usage
of Google Play Services. By default location should perform well if
you leave the Google Play Services on
Notice that you should NOT perform long operations in the background listener
callback. IOS wake-up time is limited to approximately 10 seconds and the app could
get killed if it exceeds that time slice.
Notice that the listener can also send events when the app is in the foreground,
therefore it is recommended to check the app state before deciding how to process this
event. You can use Display.isMinimized() to determine if the app is currently
running or in the background.
519
Miscellaneous Features
When implementing this make sure that:
The class passed to the API is a public class in the global scope. Not an inner class
or anything like that!
The class has a public no-argument constructor
You need to pass it as a class literal e.g. MyClassName.class . Dont use
Class.forName("my.package.MyClassName") !
Class names are problematic since device builds are obfuscated, you should only
use literals which the obfuscator detects and handles correctly.
.addGeoFencing(GeofenceListenerImpl.class, gf);
@Override
if(Display.getInstance().isMinimized()) {
Display.getInstance().callSerially(() -> {
Dialog.show("Welcome", "Thanks for arriving", "OK", null);
});
} else {
false);
Miscellaneous Features
This support isnt totally portable since the Android and iOS approaches for background
music playback differ a great deal. To get this to work on Android you need to use the
API: MediaManager.createBackgroundMedia() .
You should use that API when you want to create a media stream that will work even
when your app is minimized.
For iOS you will need to use a special build hint: ios.background_modes=music .
Which should allow background playback of music on iOS and would work with the
createBackgroundMedia() method.
Just captures and returns a path to a photo you can either open it using the Image
class or save it somewhere.
The returned file is a temporary file, you shouldnt store a reference
to it and instead copy it locally or work with the Image object
E.g. you can copy the Image to Storage using:
String filePath = Capture.capturePhoto();
if(filePath != null) {
Util.copy(FileSystemStorage.getInstance().openInputStream(filePath),
Storage.getInstance().createOutputStream(myImageFileName));
https://www.codenameone.com/javadoc/com/codename1/ui/Image.html
521
Miscellaneous Features
Style s = UIManager.getInstance().getComponentStyle("Title");
DefaultListModel<Image> m =
(DefaultListModel<Image>)iv.getImageList();
Image img = Image.createImage(filePath);
if(m == null) {
m = new DefaultListModel<>(img);
iv.setImageList(m);
iv.setImage(img);
} else {
m.addItem(img);
}
m.setSelectedIndex(m.getSize() - 1);
} catch(IOException err) {
});
Log.e(err);
hi.add(BorderLayout.CENTER, iv);
hi.show();
522
Miscellaneous Features
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_MIC, s);
FileSystemStorage fs = FileSystemStorage.getInstance();
523
Miscellaneous Features
String recordingsDir = fs.getAppHomePath() + "recordings/";
fs.mkdir(recordingsDir);
try {
MultiButton(file.substring(file.lastIndexOf("/") + 1));
mb.addActionListener((e) -> {
try {
false);
m.play();
} catch(IOException err) {
}
Log.e(err);
});
hi.add(mb);
kk-mm");
Media m = MediaManager.createMedia(filePath,
false);
m.play();
} catch(IOException err) {
}
Log.e(err);
});
hi.add(mb);
hi.revalidate();
} catch(IOException err) {
});
Log.e(err);
} catch(IOException err) {
524
Miscellaneous Features
Log.e(err);
}
hi.show();
12.6.1.Asynchronous API
The Capture API also includes a callback based API that uses the
ActionListener interface to implement capture. E.g. we can adapt the previous
sample to use this API as such:
525
Miscellaneous Features
DefaultListModel<Image> m =
(DefaultListModel<Image>)iv.getImageList();
Image img = Image.createImage((String)e.getSource());
if(m == null) {
m = new DefaultListModel<>(img);
iv.setImageList(m);
iv.setImage(img);
} else {
m.addItem(img);
}
m.setSelectedIndex(m.getSize() - 1);
} catch(IOException err) {
});
});
Log.e(err);
12.7.Gallery
The gallery API allows picking an image and/or video from the cameras gallery (camera
roll).
Like the Capture API the image returned is a temporary image
that should be copied locally, this is due to device restrictions that
dont allow direct modifications of the gallery
We can adapt the Capture sample above to use the gallery as such:
Form hi = new Form("Capture", new BorderLayout());
hi.setToolbar(new Toolbar());
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_CAMERA, s);
ImageViewer iv = new ImageViewer(icon);
hi.getToolbar().addCommandToRightBar("", icon, (ev) -> {
Display.getInstance().openGallery((e) -> {
if(e != null && e.getSource() != null) {
526
Miscellaneous Features
try {
DefaultListModel<Image> m =
(DefaultListModel<Image>)iv.getImageList();
Image img = Image.createImage((String)e.getSource());
if(m == null) {
m = new DefaultListModel<>(img);
iv.setImageList(m);
iv.setImage(img);
} else {
m.addItem(img);
}
m.setSelectedIndex(m.getSize() - 1);
} catch(IOException err) {
Log.e(err);
});
}
}
}, Display.GALLERY_IMAGE);
hi.add(BorderLayout.CENTER, iv);
content
picked
which
Display.GALLERY_VIDEO
can
or
12.8.Analytics Integration
One of the features in Codename One is builtin support for analytic instrumentation.
10
Currently Codename One has builtin support for Google Analytics , which provides
reasonable enough statistics of application usage.
Analytics is pretty seamless for the old GUI builder since navigation occurs via the
Codename One API and can be logged without developer interaction. However, to
begin the instrumentation one needs to add the line:
AnalyticsService.setAppsMode(true);
AnalyticsService.init(agent, domain);
10
https://www.google.com/analytics/
527
Miscellaneous Features
To get the value for the agent value just create a Google Analytics account and add a
domain, then copy and paste the string that looks something like UA-99999999-8 from
the console to the agent string. Once this is in place you should start receiving statistic
events for the application.
If your application is not a GUI builder application or you would like to send more
detailed data you can use the Analytics.visit() method to indicate that you are
entering a specific page.
init
method or
528
Miscellaneous Features
Codename One supports Facebooks Oauth2
iOS and Android.
11
11
https://www.codenameone.com/javadoc/com/codename1/io/Oauth2.html
529
Miscellaneous Features
Miscellaneous Features
531
Miscellaneous Features
L=[City], S=[State], C=[Country Code]" -storepass [password] -keypass
[password]
You can reuse the certificate in all your apps, some developers like
having a different certificate for every app. This is like having one
master key for all your doors, or a huge keyring filled with keys.
With the certificate we need an SHA1 key to further authenticate us to Facebook and
we do this using the keytool command line on Linux/Mac:
keytool -exportcert -alias (your_keystore_alias) -keystore
(path_to_your_keystore) | openssl sha1 -binary | openssl base64
And on Windows:
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android
\debug.keystore | openssl sha1 -binary | openssl base64
12
https://developers.facebook.com/docs/android/getting-started
532
12
Miscellaneous Features
Figure 12.16. Without flipping the switch the app wont "appear"
533
Miscellaneous Features
12.9.2.IDE Setup
We now need to set some important build hints in the project so it will work correctly.
To set the build hints just right click the project select project properties and in the
Codename One section pick the second tab. Add this entry into the table:
facebook.appId=...
The app ID will be visible in your Facebook app page in the top left position.
12.9.3.The Code
To bind your mobile app into the Facebook app you can use the following code:
Login fb = FacebookConnect.getInstance();
fb.setClientId("9999999");
fb.setRedirectURI("http://www.youruri.com/");
fb.setClientSecret("-------");
// Sets a LoginCallback listener
fb.setCallback(new LoginCallback() {
public void loginSuccessful() {
}
});
} else {
// get the token and now you can query the Facebook API
String token = fb.getAccessToken().getToken();
// ...
All of these values are from the web version of the app!
They are only used in the simulator and on "unsupported" platforms
as a fallback. Android and iOS will use the native login
534
Miscellaneous Features
13
FacebookConnect.getInstance()askPublishPermissions(new LoginCallback() {
public void loginSuccessful() {
}
// do something...
});
Notice that this wont always prompt the user, but its required to verify
that your token is valid for writing.
12.10.Google Login
We can add login to Googles social account by going to the Google developer console:
https://console.developers.google.com/
Create a new app by pressing the create button:
13
https://www.codenameone.com/javadoc/com/codename1/social/FacebookConnect.html
535
Miscellaneous Features
536
Miscellaneous Features
Miscellaneous Features
Figure 12.22. This data will be shown to users when they want to
understand who has access to their account
538
Miscellaneous Features
Figure 12.23. OAuth details, the callback URI should generally point
to your website
We now need to add Android/iOS native app bindings in much the same way as we
did the web app.
To build a native Android app make sure you setup the keystore correctly for
your application. Check out the Facebook authentication section above for a similar
discussion.
Now that you have a certificate you need two values for your app, the first is the package
name which must match the package name of your main class (the one with the start,
stop methods). It should be listed in the Codename One properties section.
539
Miscellaneous Features
Make sure to use a name that is 100% unique to you and using your own domain, dont
use the com.codename1 prefix or anything such as that
You also need the SHA1 value for your certificate, this is explained by Google here:
https://developers.google.com/+/mobile/android/getting-started
Effectively you need to run this command line:
$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/
debug.keystore -list -v
This will prompt for a password then print several lines of data one of which should start
with SHA1 that is the value that you will need for the Android app.
540
Miscellaneous Features
Miscellaneous Features
provisioning profile. If your app is correctly configured e.g. by using the Codename One
certificate wizard, then you should see it in the project properties under the Codename
OneiOS section.
542
Miscellaneous Features
12.10.1.Project Configuration
We now need to set some important build hints in the project so it will work correctly.
To set the build hints just right click the project select project properties and in the
Codename One section pick the second tab. Add these entries into the table:
ios.gplus.clientId=your ios client ID
543
Miscellaneous Features
12.10.2.The Code
The final login code is very similar to the Facebook login code
Login gc = GoogleConnect.getInstance();
gc.setClientId("*****************.apps.googleusercontent.com");
gc.setRedirectURI("https://yourURL.com/");
gc.setClientSecret("-------------------");
// Sets a LoginCallback listener
gc.setCallback(new LoginCallback() {
public void loginSuccessful() {
}
});
} else {
// get the token and now you can query the Google API
String token = gc.getAccessToken().getToken();
544
Miscellaneous Features
// ...
The clientid/secret etc. are only relevant for the simulator and should
be taken from the web app. The native login automatically connects
to the right app.
12.11.Lead Component
Codename One has two basic ways to create new components:
1. Subclass a Component override paint , implement event callbacks etc.
2. Compose multiple components into a new component, usually by subclassing a
Container .
14
Components such as Tabs subclass Container which make a lot of sense for that
component since it is physically a Container .
15
16
17
are sent to the leader, so if a label within the lead component receives a pointer pressed
event this event will really be sent to the leader.
E.g. in the case of the MultiButton the internal button will receive that event and
send the action performed event, change the state etc.
This creates some potential issues for instance in MultiButton :
14
https://www.codenameone.com/javadoc/com/codename1/ui/Tabs.html
15
https://www.codenameone.com/javadoc/com/codename1/components/MultiButton.html
16
https://www.codenameone.com/javadoc/com/codename1/components/SpanButton.html
17
https://www.codenameone.com/javadoc/com/codename1/components/SpanLabel.html
545
Miscellaneous Features
myMultiButton.addActionListener((e) -> {
if(e.getComponent() == myMultiButton) {
}
if(e.getActualComponent() == myMultiButton) {
});
The leader also determines the style state, so all the elements being lead are in the
same state. E.g. if the the button is pressed all elements will display their pressed
states, notice that they will do so with their own styles but they will each pick the pressed
version of that style so a Label UIID within a lead component in the pressed state
would return the Pressed state for a Label not for the Button .
This is very convenient when you need to construct more elaborate UIs and the cool
thing about it is that you can do this entirely in the designer which allows assembling
containers and defining the lead component inside the hierarchy.
E.g. the SpanButton class is very similar to this code:
public class SpanButton extends Container {
private Button actualButton;
private TextArea text;
setLayout(new BorderLayout());
addComponent(BorderLayout.WEST, actualButton);
addComponent(BorderLayout.CENTER, text);
setLeadComponent(actualButton);
text.setText(getUIManager().localize(t, t));
546
Miscellaneous Features
public void setIcon(Image i) {
}
actualButton.setIcon(i);
return text.getText();
return actualButton.getIcon();
actualButton.addActionListener(l);
actualButton.removeActionListener(l);
f.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_ADD, e
-> addEntry(accr));
addEntry(accr);
f.add(BorderLayout.CENTER, accr);
f.show();
547
Miscellaneous Features
void addEntry(Accordion accr) {
FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE);
Label title = new Label(t.getText());
This allows us to add/edit entries but it also allows the delete button above to actually
work separately. Without a call to setBlockLead(true) the delete button would cat
as the rest of the accordion title.
548
Miscellaneous Features
Figure 12.27. Accordion with delete button entries that work despite
the surrounding lead
12.12.Pull To Refresh
Pull to refresh is the common UI paradigm that Twitter popularized where the user
can pull down the form/container to receive an update. Adding this to Codename One
couldnt be simpler!
Just invoke addPullToRefresh(Runnable) on a scrollable container (or form) and
the runnable method will be invoked when the refresh operation occurs.
Pull
to
refresh
is
InifiniteContainer
implicitly
implements
L10NManager.getInstance().formatDateTimeShort(new Date()));
});
hi.show();
549
in
the
Miscellaneous Features
The Display classs execute method allows us to invoke a URL which is bound
to a particular application.
18
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
550
Miscellaneous Features
19
This works rather well assuming the application is installed. E.g. this list contains a
set of valid URLs that can be used on iOS to run common applications and use builtin
functionality.
Some URLs might not be supported if an app isnt installed, on Android there isnt
much that can be done but iOS has a canOpenURL method for Objective-C.
On iOS you can use the Display.canExecute() method which returns a
Boolean instead of a boolean which allows us to support 3 result states:
1. Boolean.TRUE - the URL can be executed
2. Boolean.FALSE - the URL isnt supported or the app is missing
3. null - we have no idea whether the URL will work on this platform.
The sample below launches a "godfather" search on IMDB only when this is sure to
work (only on iOS currently). We can actually try to search in the case of null as well
but this sample plays it safe by using the http link which is sure to work:
Boolean can = Display.getInstance().canExecute("imdb:///find?
q=godfather");
if(can != null && can) {
Display.getInstance().execute("imdb:///find?q=godfather");
} else {
}
19
Display.getInstance().execute("http://www.imdb.com");
http://wiki.akosma.com/IPhone_URL_Schemes
551
Figure 13.1. Image sizes window that allows us to find the biggest
impact on our RAM/Storage
This produces a list of images sorted by size with their sizes. Often the top entries will
be multi-images, which include HD resolution values that can be pretty large. These
very high-resolution images take up a significant amount of space!
552
553
You can use the excellent OptiPng tool to optimize image files right from the
Codename One designer. To use this feature you need to install OptiPng then select
Images Launch OptiPng from the menu. Once you do that the tool will automatically
optimize all your PNGs.
When faced with size issues make sure to check the size of your res file, if your JAR
file is large open it with a tool such as 7-zip and sort elements by size. Start reviewing
which element justifies the size overhead.
13.2.Improving Performance
There are quite a few things you can do as a developer in order to improve the
performance and memory footprint of a Codename One application. This sometimes
depends on specific device behaviors but some of the tips here are true for all devices.
The simulator contains some tools to measure performance overhead of a specific
component and also detect EDT blocking logic. Other than that follow these guidelines
to create more performance code:
Avoid round rect borders - they have a huge overhead on all platforms. Use image
borders instead (counter intuitively they are MUCH faster)
Avoid Gradients - they perform poorly on most OSs. Use a background image
instead
Use larger images when tiling or building image borders, using a 1 pixel (or event
a few pixels) wide or high image and tiling it repeatedly can be very expensive
Shrink resource file sizes - Otherwise data might get collected by the garbage
collector and reloading data might be expensive
1
http://optipng.sourceforge.net/
554
13.3.Performance Monitor
The Performance Monitor tool can be accessible via the Simulator Performance
Monitor menu option in the simulator. This launches the following UI that can help you
improve application performance:
Figure 13.4. Main tab of the performance monitor: Logs and timings
The first tab of the performance monitor includes a table of the drawn components.
Each entry includes the number of times it was drawn and the slowest/fastest and
average drawing time.
555
556
13.4.Network Speed
https://www.codenameone.com/
https://github.com/codenameone/cn1-binaries/archive/master.zip
557
Figure 13.8. Test recording in progress, when done just press the
save icon
You can build tests using the Codename One testing package to manipulate the
Codename One UI programmatically and perform various assertions.
Unlike frameworks such as JUnit which assign a method per test, the Codename One
test framework uses a class per test. This allows the framework to avoid reflection and
thus allows it to work properly on the device.
559
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
560
561
562
563
564
565
Figure 14.4. The GUI based settings might conflict with some of the
build hints
Here is the current list of supported arguments, notice that build hints are added all the
time so consult the discussion forum if you dont find what you need here:
Description
android.debug
android.release
android.installLocation
Description
android.gradle
android.xapplication
android.xpermissions
android.xintent_filter
android.licenseKey
android.stack_size
android.statusbar_hidden
android.facebook_permissions
android.googleAdUnitId
android.googleAdUnitTestDevice
android.includeGPlayServices
Deprecated, please
android.playService.*! Indicates
whether Goolge Play Services should
be included into the build, defaults to
false but that might change based on the
567
Description
functionality of the application and other
build hints. Adding Google Play Services
support allows us to use a more refined
location implementation and invoke
some Google specific functionality from
native code.
android.playService.plus,
android.playService.auth,
android.playService.base,
android.playService.identity,
android.playService.indexing,
android.playService.appInvite,
android.playService.analytics,
android.playService.cast,
android.playService.gcm,
android.playService.drive,
android.playService.fitness,
android.playService.location,
android.playService.maps,
android.playService.ads,
android.playService.vision,
android.playService.nearby,
android.playService.panorama,
android.playService.games,
android.playService.safetynet,
android.playService.wallet,
android.playService.wearable
android.includeGPlayServices
and only works with the gradle build
(which is on by default but can be
toggled using android.gradle ).
android.headphoneCallback
headphonesConnected &
headphonesDisconnected which it
invokes appropriately as needed
android.gpsPermission
568
Description
However, some code might want to
explicitly define it
android.asyncPaint
android.stringsXml
android.supportV4
android.style
android.cusom_layout1
android.keyboardOpen
android.versionCode
569
Description
version number specifically used for the
xml attribute android:versionCode
android.captureRecord
android.nonconsumable
android.removeBasePermissions
android.smallScreens
android.xactivity
android.streamMode
570
Description
android.pushVibratePattern
android.enableProguard
android.proguardKeep
com.mypackage.ProblemClass
{ *; }
android.sharedUserId
android.sharedUserLabel
android.targetSDKVersion
android.theme
android.web_loading_hidden
block_server_registration
571
Description
register with our server, setting this
to true blocks them from sending
information to our cloud. We keep this
data for statistical purposes and intend
to provide additional installation stats in
the future.
facebook.appId
ios.bitcode
ios.keyboardOpen
ios.urlScheme
Allows intercepting a
URL call using the syntax
<string>urlPrefix<string>
ios.project_type
ios.statusbar_hidden
ios.newStorageLocation
572
Description
correct but might break compatibility.
1
This is described in this issue
ios.prerendered_icon
ios.application_exits
ios.themeMode
default/legacy/modern/auto (defaults
to default). Default means you dont
define a theme mode. Currently this
is equivalent to legacy. In the future
we will switch this to be equivalent to
auto. legacy - this will behave like iOS 6
regardless of the device you are running
on. modern - this will behave like iOS 7
regardless of the device you are running
on. auto - this will behave like iOS 6
on older devices and iOS 7 on newer
devices.
ios.interface_orientation
UIInterfaceOrientationPortrait by
default. Indicates the orientation, one
or more of (separated by colon :):
UIInterfaceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft,
UIInterfaceOrientationLandscapeRight.
Notice that the IDE plugin has an
https://github.com/codenameone/CodenameOne/issues/1480
573
Description
"Interface Orientation" combo box you
should use under the iOS section.
ios.xcode_version
java.version
javascript.inject_proxy
javascript.minifying
javascript.proxy.url
javascript.sourceFilesCopied
574
Description
resulting .zip and .war files. These may
be used by Chrome during debugging.
javascript.stopOnErrors
javascript.teavm.version
rim.askPermissions
google.adUnitId
rim.ignor_legacy
https://www.codenameone.com/blog/adding-google-play-ads.html
575
Description
rim.obfuscation
ios.plistInject
ios.includePush
ios.newPipeline
ios.headphoneCallback
ios.facebook_permissions
ios.applicationDidEnterBackground
ios.enableAutoplayVideo
ios.googleAdUnitId
ios.viewDidLoad
576
Description
ios.googleAdUnitIdPadding
ios.enableBadgeClear
ios.glAppDelegateHeader
ios.glAppDelegateBody
ios.beforeFinishLaunching
ios.afterFinishLaunching
ios.locationUsageDescription
577
Description
ios.add_libs
ios.pods
ios.objC
ios.testFlight
desktop.width
desktop.height
desktop.adaptToRetina
https://cocoapods.org/
578
Description
desktop.resizable
desktop.fontSizes
desktop.theme
desktop.themeMac
desktop.themeWin
desktop.windowsOutput
noExtraResources
j2me.iconSize
14.2.1.Basic Concepts
The Codename One Designer isnt a standard code generator; the UI is saved within the
resource file and can be designed without the source files available. This has several
advantages:
1. No fragile generated code to break.
2. Designers who dont know Java can use the tool.
3. The "Codename One LIVE!" application can show a live preview of your design as
you build it.
4. Images and theme settings can be integrated directly with the GUI without concern.
5. The tool is consistent since the file you save is the file you run.
6. GUIs/themes can be downloaded dynamically without replacing the application
(this can reduce download size).
7. It allows for control over application flow. It allows preview within the tool without
compilation.
This does present some disadvantages and oddities:
1. Its harder to integrate custom code into the GUI builder/designer tool.
2. The tool is somewhat opaque; there is no "code" you can inspect to see what was
accomplished by the tool.
3. If the resource file grows too large it can significantly impact memory/performance
of a running application.
4. Binding between code and GUI isnt as intuitive and is mostly centralized in a single
class.
In theory you dont need to generate any code, you can load any resource file that
contains a UI element as you would normally load a Resource file:
Resources r = Resources.open("/myFile.res");
580
(Notice that since Form & Dialog both derive from Container you can just downcast
to the appropriate type).
This would work for any resource file and can work completely dynamically! E.g. you
can download a resource file on the fly and just show the UI that is within the resource
8
file That is what Codename One LIVE! is doing internally.
14.2.2.IDE Bindings
While the option of creating a Resource file manually is powerful, its not nearly as
convenient as modern GUI builders allow. Developers expect the ability to override
events and basic behavior directly from the GUI builder and in mobile applications even
the flow for some cases.
To facilitate IDE integration we decided on using a single Statemachine class,
similar to the common controller pattern. We considered multiple classes for every
form/dialog/container and eventually decided this would make code generation more
cumbersome.
The designer effectively generates one class StatemachineBase which is a
9
subclass of UIBuilder (you can change the name/package of the class in the
Codename One properties file at the root of the project). StatemachineBase is
generated every time the resource file is saved assuming that the resource file is within
the src directory of a Codename One project. Since the state machine base class is
always generated, all changes made into it will be overwritten without prompting the
user.
10
User code is placed within the Statemachine class, which is a subclass of the
11
Statemachine Base class. Hence it is a subclass of UIBuilder !
4
5
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
https://www.codenameone.com/javadoc/com/codename1/ui/Form.html
6
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html
7
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
8
https://www.codenameone.com/codename-one-live.html
9
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
10
https://www.codenameone.com/javadoc/com/codename1/facebook/User.html
11
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
581
find a component instance within the container hierarch. Effectively this is a shortcut
syntax for UIBuilder.findByName() , its still useful since the method is type
safe. Hence if a resource component name is changed the find() method will fail
in subsequent compilations.
12
2. Callback
events - these are various callback methods with common names
e.g.: onCreateFormX() , beforeFormX() etc. These will be invoked when a
particular event/behavior occurs.
Within the GUI builder, the event buttons would be enabled and the GUI builder
provides a quick and dirty way to just override these methods. To prevent a future case
in which the underlying resource file will be changed (e.g formX could be renamed to
formY) a super method is invoked e.g. super. onCreateFormX() ;
This will probably be replaced with the @Override annotation when Java 5 features
are integrated into Codename One.
// If the resource file changes the names of components this call will
super.onMainUI_RemoveModeButtonAction(c, event);
removeMode = !removeMode;
Container friendRoot = findFriendsRoot(c.getParent());
Dimension size = null;
if(removeMode) {
{
findRemoveModeButton(c.getParent()).setText("Finish");
} else {
12
https://www.codenameone.com/javadoc/com/codename1/util/Callback.html
582
findRemoveModeButton(c.getParent()).setText("Remove");
Container currentFriend =
(Container)friendRoot.getComponentAt(iter);
currentFriend.setShouldCalcPreferredSize(true);
currentFriend.setFocusable(!removeMode);
findRemoveFriend(currentFriend).setPreferredSize(size);
currentFriend.animateLayout(800);
}
As you can see from the code above implementing some basic
callbacks within the state machine is rather simple. The method
findFriendsRoot(c.getParent()); is used to find the "FriendsRoot"
component within the hierarchy, notice that we just pass the parent container to the
finder method. If the finder method doesnt find the friend root under the parent it will
find the "true" root component and search there.
The friends root is a container that contains the full list of our "friends" and within
it we can just work with the components that were instantiated by the GUI builder.
Implementing Custom Components There are two basic approaches for custom
components:
1. Override a specific type - e.g. make all Forms derive a common base class.
2. Replace a deployed instance.
The first uses a feature of UIBuilder
13
if(cls == Form.class) {
}
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
583
This code allows me to create a unified global form subclass. Thats very useful when I
want so global system level functionality that isnt supported by the designer normally.
The second approach allows me to replace an existing component:
protected void beforeSplash(Form f) {
super.beforeSplash(f);
splashTitle = findTitleArea(f);
// create a "slide in" effect for the title
dummyTitle = new Label();
dummyTitle.setPreferredSize(splashTitle.getPreferredSize());
f.replace(splashTitle, dummyTitle, null);
f.replace(dummyTitle, splashTitle,
CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL,
true, 1000));
splashTitle = null;
dummyTitle = null;
Notice that we replace the title with an empty label; in this case we do this so we can later
replace it while animating the replace behavior thus creating a slide-in effect within the
title. It can be replaced though, for every purpose including the purpose of a completely
different custom made component. By using the replace method the existing layout
constraints are automatically maintained.
14.3.Android Permissions
One of the annoying tasks when programming native Android applications is tuning all
the required permissions to match your codes requirements, Codename One aims to
simplify this. The build server automatically introspects the classes sent to it as part of
the build and injects the right set of permissions required by the app.
However, sometimes developers might find the permissions that come up a bit
confusing and might not understand why a specific permission came up. This maps
584
is
triggered
by
usage
of
&
android.permission.READ_PHONE_STATE
is
triggered
by
com.codename1.ads
package,
com.codename1.components.Ads ,
com.codename1.components.ShareButton ,
com.codename1.media ,
com.codename1.push , Display.getUdid() & Display.getMsisdn() . This
permission is required for media in order to suspend audio playback when you get a
phone call.
android.hardware.location ,
android.hardware.location.gps ,
android.permission.ACCESS_FINE_LOCATION ,
android.permission.ACCESS_MOCK_LOCATION
&
android.permission.ACCESS_COARSE_LOCATION
map
to
com.codename1.maps & com.codename1.location .
package.permission.C2D_MESSAGE ,
com.google.android.c2dm.permission.RECEIVE ,
android.permission.RECEIVE_BOOT_COMPLETED - are requested by the
com.codename1.push package
android.permission.READ_CONTACTS
triggers
by
the
com.codename1.contacts & Display.getAllContacts() .
package
is
triggered
by
Display.deleteContact() ,
&
Enabling Permissions
Codenmae One compiles Android targets with SDK level 23 but not with target level 23!
This means that by default the new permission mode is still off and you wont see any
of the effects mentioned below.
This will probably change to the default in the future but at the
moment the target SDK defaults to 21
To activate this functionality you will need to set the target SDK to level 23 by using the
android.targetSDKVersion=23 build hint.
Permission Prompts
To test this API see the following simple contacts app:
Form f = new Form("Contacts", BoxLayout.y());
586
Display.getInstance().invokeAndBlock(() -> {
Contact[] ct = Display.getInstance().getAllContacts(true, true, false,
true, true, false);
Display.getInstance().callSerially(() -> {
f.removeAll();
for(Contact c : ct) {
});
});
}
f.revalidate();
f.show();
When we try to install this app without changing anything on an Android 6 device we
see this UI:
587
589
591
593
595
Code Changes
There are no explicit code changes needed for this functionality to "just work". The
respective APIs will work just like they always worked and will prompt the user
seamlessly for permissions.
Some behaviors that never occurred on Android but were perfectly
legal in the past might start occurring with the switch to the new API.
E.g. the location manager might be null and your app must always
be ready to deal with such a situation
When permission is requested a user will be seamlessly prompted/warned, Codename
One has builtin text to control such prompts but you might want to customize the text.
You can customize permission text via the Display properties e.g. to customize the
text of the contacts permission we can do something such as:
Display.getInstance().setProperty("android.permission.READ_CONTACTS", "MyCoolChatApp
needs access to your contacts so we can show you which of your friends
already have MyCoolChatApp installed");
This is optional as there is a default value defined. You can define this once in the
init(Object) method but for some extreme cases permission might be needed for
different things e.g. you might ask for this permission with one reason at one point in
the app and with a different reason at another point in the app.
The
following
permission
"android.permission.READ_PHONE_STATE"
keys
are
supported:
android.permission.WRITE_EXTERNAL_STORAGE ,
android.permission.ACCESS_FINE_LOCATION ,
android.permission.SEND_SMS , android.permission.READ_CONTACTS ,
android.permission.WRITE_CONTACTS ,
android.permission.RECORD_AUDIO .
Simulating Prompts
You can simulate permission prompts by checking that option in the simulator menu.
597
AndroidNativeUtils checkForPermission
If you write Android native code using native interfaces you are probably familiar
with the AndroidNativeUtil class from the com.codename1.impl.android
package.
This class provides access to many low level capabilities you would need as a
developer writing native code. Since native code might need to request a permission
we introduced the same underlying logic we used namely: checkForPermission .
To get a permission you can use this code as such:
if(!
com.codename1.impl.android.AndroidNativeUtil.checkForPermission(Manifest.permission.READ
should be the description shown to the user...")){
}
// you didn't get the permission, you might want to return here
This will prompt the user with the native UI and later on with the fallback option
as described above. Notice that the checkForPermission method is a blocking
method and it will return when there is a final conclusion on the subject. It uses
598
14.5.Native Interfaces
Sometimes you may wish to use an API that is unsupported by Codename One or
integrate with a 3rd party library/framework that isnt supported. These are achievable
tasks when writing native code and Codename One lets you encapsulate such native
code using native interfaces.
Notice that when we say "native" we do not mean C/C++ always but rather the platforms
"native" environment. So in the case of Android the Java code will be invoked with full
access to the Android API, in case of iOS an Objective-C message would be sent and
so forth.
600
1. Creating an interface that extends NativeInterface and only defines methods with
the arguments/return values declared in the previous paragraph.
2. Creating the proper native implementation hierarchy based on the call conventions
for every platform within the native directory
E.g. to create a simple hello world interface do something like:
package com.mycompany.myapp;
import com.codename1.system.NativeInterface;
We now need to right click the class in the IDE and select the Generate Native Access
menu item:
14
15
https://www.codenameone.com/javadoc/com/codename1/ui/PeerComponent.html
https://www.codenameone.com/javadoc/com/codename1/system/NativeInterface.html
601
Figure 14.13. Once generated we are prompted that the native code
is in the "native" directory
We can now look int the native directory in the project root (in NetBeans you can see
that in the Files tab) and you can see something that looks like this:
602
603
Log.p(my.helloWorld("Hi"));
Notice that for this to work you must implement the native code on all supported
platforms.
Well start with Android which should be familiar and intuitive to many developers, this
is how the generated file under the native/android directory looks:
package com.mycompany.myapp;
public class MyNativeImpl {
return null;
return false;
604
NativeInterface on some platforms and leave the rest out without really knowing
anything about these platforms.
We can implement the Android version using code similar to this:
package com.mycompany.myapp;
import android.util.Log;
public class MyNativeImpl {
public String helloWorld(String param) {
Log.d("MyApp", param);
return "Tada";
return true;
Notice that we are using the Android native android.util.Log class which
isnt accessible from standard Codename One code
The impl class doesnt physically implement the MyNative interface!
This is intentional and due to the PeerComponent functionality mentioned
below. You dont need to add an implements clause.
Notice that there is no constructor and the class is public. It is crucial that the
system will be able to allocate the class without obstruction. You can use a
constructor but it cant have any arguments and you shouldnt rely on semantics
of construction.
We implemented the native method and that we set isSupported to true.
The IDE wont provide completion suggestions and will claim that
there are errors in the code!
Codename One doesnt include the native platforms in its bundle
e.g. the full Android SDK or the full xcode Objective-C runtime.
However, since the native code is compiled on the servers (where
these runteims are present) this shouldnt be a problem
605
14.5.1.Objective-C (iOS)
When generating the Objective-C code the "Generate Native Sources"
tool produces two files:
com_mycompany_myapp_MyNativeImpl.h
&
com_mycompany_myapp_MyNativeImpl.m .
The
.m
files are the Objective-C equivalent
.h
files contain the header/include information.
com_mycompany_myapp_MyNativeImpl.h contains:
of
In
#import <Foundation/Foundation.h>
@interface com_mycompany_myapp_MyNativeImpl : NSObject {
}
-(NSString*)helloWorld:(NSString*)param;
-(BOOL)isSupported;
@end
606
.c
this
files
case
and
the
return @"Tada";
-(BOOL)isSupported{
return YES;
}
@end
14.5.2.Javascript
Native interfaces in Javascript look a little different than the other platforms since
Javascript doesnt natively support threads or classes. The native implementation
should be placed in a file with name matching the name of the package and the class
name combined where the "." elements are replaced by underscores.
The default generated stubs for
com_mycompany_myapp_MyNative :
the
JavaScript
build
look
(function(exports){
var o = {};
o.helloWorld__java_lang_String = function(param1, callback) {
};
607
like
this
callback.complete(false);
exports.com_mycompany_myapp_MyNative= o;
})(cn1_get_native_interfaces());
callback.complete("Hello World!!!");
o.isSupported_ = function(callback) {
};
callback.complete(true);
exports.com_my_code_MyNative = o;
})(cn1_get_native_interfaces());
Notice that we use the complete() method of the provided callback to pass the
return value rather than using the return statement. This is to work around the
fact that Javascript doesnt natively support threads. The Java thread that is calling
your native interface will block until your method calls callback.complete() . This
allows you to use asynchronous APIs inside your native method while still allowing
Codename One to work use your native interface via a synchronous API.
Make sure you call either callback.complete() or
callback.error() in your method at some point, or you will
cause a deadlock in your app (code calling your native method will
just sit and "wait" forever for your method to return a value).
The naming conventions for the methods themselves are modeled after the naming
conventions shown in the previous examples:
<method-name>__<param-1-type>_<param-2-type>_<param-n-type>
608
JavaScript Examples
Java API:
public void print(String str);
becomes
o.print__java_lang_String = function(param1, callback) {
console.log(param1);
callback.complete();
Java API:
public int add(int a, int b);
becomes
o.add__int_int = function(param1, param2, callback) {
}
callback.complete(param1 + param2);
609
c += param1[i];
}
callback.complete(c);
return null;
16
https://www.codenameone.com/javadoc/com/codename1/ui/PeerComponent.html
610
blue'});
callback.complete(c.get(0));
};
Notice that if you want to use a native library (jar, .a file etc.) just places it within the
appropriate native directory and it will be packaged into the final executable. You would
only be able to reference it from the native code and not from the Codename One code,
which means you will need to build native interfaces to access it.
This is discussed further below.
Android
RIM
JavaSE
Obj-C
C#
byte
byte
byte
byte
char
sbyte
boolean
boolean
boolean
boolean
BOOL
bool
char
char
char
char
int
char
short
short
short
short
short
short
int
int
int
int
int
int
611
Android
RIM
JavaSE
Obj-C
C#
long
long
long
long
long long
long
float
float
float
float
float
float
double
double
double
double
double
double
String
String
String
String
NSString*
String
byte[]
byte[]
byte[]
byte[]
NSSData*
sbyte[]
boolean[]
boolean[]
boolean[]
boolean[]
NSData*
bool[]
char[]
char[]
char[]
char[]
NSData
char[]
short[]
short[]
short[]
short[]
NSData*
short[]
int[]
int[]
int[]
int[]
NSData*
int[]
long[]
long[]
long[]
long[]
NSData*
long[]
float[]
float[]
float[]
float[]
NSData*
float[]
double[]
double[]
double[]
double[]
NSData*
double[]
PeerComponent
android.view.View
net.rim.device.api.ui.Field
PeerComponent
void*
FrameworkElement
byte[] ba, boolean[] booa, char[] ca, short[] sa, int[] ia, long[]
Android Version
public void test(byte param, boolean param1, char param2, short
param3, int param4, long param5, float param6, double param7, String
param8, byte[] param9, boolean[] param10, char[] param11, short[]
param12, int[] param13, long[] param14, float[] param15, double[]
param16, android.view.View param17) {
612
iOS Version
-(void)test:(char)param param1:(BOOL)param1 param2:(int)param2 param3:
(short)param3 param4:(int)param4 param5:(long long)param5 param6:
(float)param6 param7:(double)param7 param8:(NSString*)param8 param9:
(NSData*)param9 param10:(NSData*)param10 param11:(NSData*)param11 param12:
(NSData*)param12 param13:(NSData*)param13 param14:(NSData*)param14
param15:(NSData*)param15 param16:(NSData*)param16 param17:(void*)param17;
}
JavaScript Version
o.test__byte_boolean_char_short_int_long_float_double_java_lang_String_byte_1ARRAY_boole
= function(param1, param2, param3, param4, param5, param6, param7,
Java SE Version
public void test(byte param, boolean param1, char param2, short
param3, int param4, long param5, float param6, double param7, String
param8, byte[] param9, boolean[] param10, char[] param11, short[]
param12, int[] param13, long[] param14, float[] param15, double[]
param16, com.codename1.ui.PeerComponent param17) {
RIM/Blackberry Version
public void test(byte param, boolean param1, char param2, short
param3, int param4, long param5, float param6, double param7, String
param8, byte[] param9, boolean[] param10, char[] param11, short[]
param12, int[] param13, long[] param14, float[] param15, double[]
param16, net.rim.device.api.ui.Field param17) {
Java ME Version
public void test(byte param, boolean param1, char param2, short
param3, int param4, long param5, float param6, double param7, String
param8, byte[] param9, boolean[] param10, char[] param11, short[]
613
C# Version
public void test(byte param, bool param1, char param2, short param3, int
param4, long param5, float param6, double param7, String param8, byte[]
param9, boolean[] param10, char[] param11, short[] param12, int[]
param13, long[] param14, float[] param15, double[] param16,
FrameworkElement param17) {
You need to include the full XML snippet. You can unify multiple lines
into a single line in the GUI as XML allows that.
14.5.6.Native AndroidNativeUtil
If you do any native interfaces programming in Android you should be familiar with the
AndroidNativeUtil class which allows you to access native device functionality
more easily from the native code. E.g. many Android APIs need access to the
Activity which you can get by calling AndroidNativeUtil.getActivity() .
The native util class includes quite a few other features such as:
614
AndroidNativeUtil.getActivity().runOnUiThread(new Runnable() {
public void run() {
});
...
}
....
https://www.codenameone.com/javadoc/com/codename1/ui/PeerComponent.html
https://www.codenameone.com/javadoc/com/codename1/ui/Dialog.html
615
// do stuff
So if I want to call it from Android or all of the Java based platforms I can just write
this in the "native" code:
com.mycompany.NativeCallback.callback();
The VM passes the thread context along method calls to save on API calls (thread
context is heavily used in Java for synchronization, gc and more).
616
Why No Comma?
The comma is included as part of the macro which makes for code that isnt as
readable.
19
You would need to convert the NSString* value you already have to a
java.lang.String which the callback expects.
The fromNSString function also needs this special argument so you will need to
modify the method as such:
com_mycompany_NativeCallback_callback___java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG
fromNSString(CN1_THREAD_GET_STATE_PASS_ARG nsStringValue));
And finally you might want to return a value from callback as such:
19
617
This is tricky since the method name changes to support covariant return types and
so the signature would be:
com_mycompany_NativeCallback_callback___int_R_int(intValue);
$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double;
google.maps.event.addListener(this.map, 'bounds_changed', function() {
fireMapChangeEvent(self.mapId, self.map.getZoom(),
self.map.getCenter().lat(), self.map.getCenter().lng());
});
618
$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double;
to
this.$GLOBAL
$.com_codename1_googlemaps_MapContainer.fireMapChangeEvent__int_int_double_double
$async;
This will cause the call to be wrapped in the appropriate bootstrap code to work properly
with threads - and it is absolutely necessary in cases where the method may use
threads of any kind. The side-effect of calling a method with the $async suffix is that
you cant use return values from the method.
In most cases you should use the async version of a method
when calling it from your native method. Only use the synchronous
(default) version if you are absolutely sure that the method doesnt
use any threading primitives.
619
14.6.Libraries - cn1lib
Support for JAR files in Codename One has been a source of confusion so its probably
a good idea to revisit this subject again and clarify all the details.
The first source of confusion is changing the classpath. You should NEVER change
the classpath or add an external JAR via the IDE classpath UI. The reasoning here is
very simple, these IDEs dont package the JARs into the final executable and even if
they did these JARs would probably use features unavailable or inappropriate for the
device (e.g. java.io.File etc.).
Figure 14.16. Dont change the classpath, this is how it should look
for a typical Java 8 Codename One application
Cn1libs are Codename Ones file format for 3rd party extensions. Its physicially a zip
file containing other zip files and some meta-data.
620
Codename One has a large repository of 3rd party cn1libs , you can install a cn1lib by
placing it in the lib directory of your project then right clicking the project and selecting
Codename One Refresh cn1lib files.
20
https://www.codenameone.com/cn1libs.html
621
622
623
624
hint
as
something
like
Notice that this can still collide e.g. if a different cn1lib defines its own background
mode. However, there are many valid cases where ios.plistInject can be used
for other things. In this case well append the content of the ios.plistInject into
the build hint if its not already there.
There are a couple of things you need to keep in mind:
Properties are merged with every "refresh libs" call not dynamically on the server.
This means it should be pretty simple for the developer to investigate issues in this
process.
Changing flags is problematic - there is no "uninstall" process. Since the data
is copied into the codenameone_settings.properties file. If you need to
change a flag later on you might need to alert users to make changes to their
properties essentially negating the value of this feature
So be very careful when adding properties here.
Its your responsibility as a library developer to decide which build hint goes into which
file!
625
android.googleAdUnitId
android.includeGPlayServices
android.headphoneCallback
android.gpsPermission
android.asyncPaint
android.supportV4
android.theme
android.cusom_layout1
android.versionCode
android.captureRecord
android.removeBasePermissions
android.blockExternalStoragePermission
android.min_sdk_version
android.smallScreens
android.streamMode
android.enableProguard
android.targetSDKVersion
android.web_loading_hidden
facebook.appId
ios.keyboardOpen
ios.project_type
ios.newStorageLocation
ios.prerendered_icon
ios.application_exits
ios.themeMode
ios.xcode_version
javascript.inject_proxy
javascript.minifying
javascript.proxy.url
javascript.sourceFilesCopied
626
ios.headphoneCallback
ios.enableAutoplayVideo
ios.googleAdUnitId
ios.googleAdUnitIdPadding
ios.enableBadgeClear
ios.locationUsageDescription
ios.bundleVersion
ios.objC
ios.testFlight
desktop.width
desktop.height
desktop.adaptToRetina
desktop.resizable
desktop.fontSizes
desktop.theme
desktop.themeMac
desktop.themeWin
desktop.windowsOutput
noExtraResources
j2me.iconSize
android.nonconsumable
android.xapplication_attr
android.xactivity
android.pushVibratePattern
android.proguardKeep
android.sharedUserId
android.sharedUserLabel
ios.urlScheme
ios.interface_orientation
ios.plistInject
ios.facebook_permissions
ios.applicationDidEnterBackground
627
ios.glAppDelegateHeader
ios.glAppDelegateBody
ios.beforeFinishLaunching
ios.afterFinishLaunching
ios.add_libs
Required
Purpose
main.zip
stubs.zip
manifest.properties
codenameone_ library_
appended.properties
Discussed above
codenameone_ library_
required.properties
Discussed above
nativeios.zip
nativeand.zip
nativejavascript.zip
nativerim.zip
nativese.zip
nativewin.zip
628
Required
Purpose
Native Java ME sources if applicable
http://tools.android.com/tech-docs/new-build-system/aar-format
629
String[] buttons = {"A Game of Thrones", "A Clash Of Kings", "A Storm Of
Swords",
"A Feast For Crows", "A Dance With Dragons", "The Winds of Winter", "A
Dream of Spring" };
Container box = new Container(BoxLayout.y());
box.setScrollableY(true);
box.setDropTarget(true);
java.util.List<String> got = Arrays.asList(buttons);
Collections.shuffle(got);
for(String current : got) {
box.add(mb);
mb.setDraggable(true);
630
This allows us to build a JavaScript version of the app automatically as part of a release
build script.
<color name="colorPrimaryDark">#80ff0000</color>
<color name="colorAccent">#800000ff</color>
</resources>
https://www.codenameone.com/javadoc/com/codename1/ui/Toolbar.html
https://www.codenameone.com/javadoc/com/codename1/ui/SideMenuBar.html
633
your application to a different application running on the device. This would allow that
application to launch your application.
This isnt something we builtin to Codename One, however it does expose enough of
the platform capabilities to enable that functionality rather easily on Android.
On Android we need to define an intent filter which we can do using the
android.xintent_filter build hint, this accepts the XML to filter whether a
request is relevant to our application:
android.xintent_filter=<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
24
To bind the myapp:// URL to your application. As a result typing myapp://x into
the Android browser will launch the application.
This value would be null if the app was launched via the icon.
iOS is practically identical to Android with some small caveats, iOSs equivalent of the
manifest is the plist.
You can inject more data into the plist by using the ios.plistInject build hint.
So the equivalent in the iOS side would be
ios.plistInject=<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
24
http://stackoverflow.com/questions/11421048/android-ios-custom-uri-protocol-handling
634
<key>CFBundleURLSchemes</key>
<string>myapp</string>
</array>
</dict>
<array>
</dict>
<dict>
</array>
However, that can conflict with the Facebook integration if you use
FacebookConnect which needs access to the schemes. To workaround it you can
use the build hint ios.urlScheme e.g.:
ios.urlScheme=<string>myapp</string>
28
26
| API Docs
| API Docs
27
29
637
30
for the native Android SDK. If the class hierarchy doesnt look too elaborate, we may
decide to model our Codename One public API fairly closely on the Android API. On
the other hand, if we only need a small part of the SDKs functionality, we may choose
to create my abstractions around just the functionality that we need.
In the case of the FreshDesk SDK, it looks like most of the functionality is handled by
one central class Mobihelp , with a few other POJO classes for passing data to and
from the service. This is a good candidate for a comprehensive Codename One API.
Before proceeding, we also need to look at the iOS API to see if there are any features
that arent included. While naming conventions in the iOS API are a little different than
those in the Android API, it looks like they are functionally the same.
Therefore, I choose to create a class hierarchy and API that closely mirrors the Android
SDK.
http://developer.freshdesk.com/mobihelp/android/api/reference/com/freshdesk/mobihelp/package-
summary.html
638
31
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/Mobihelp.java
32
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/FeedbackRequest.java
Figure 14.22. Freshdesk API Integration
33
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/FeedbackType.java
34
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/MobihelpConfig.java
640
Things to Notice
31
1. The public API consists of the main class ( Mobihelp ), and a few supporting
32
33
34
classes ( FeedbackRequest , FeedbackType , MobihelpConfig ,
35
MobihelpCallbackStatus ), which were copied almost directly from the
Android SDK.
2. The only way for the public API to communicate with the native SDK is via the
36
MobihelpNative
interface.
37
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/MobihelpCallbackStatus.java
36
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/MobihelpNative.java
37
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/MobihelpNativeCallback.java
38
http://developer.freshdesk.com/mobihelp/android/api/reference/com/freshdesk/mobihelp/Mobihelp.html
39
http://developer.freshdesk.com/mobihelp/android/api/reference/com/freshdesk/mobihelp/Mobihelp.html
641
Non-Primitive Parameters
Although our public API isnt constrained by the same rules as our Native Interfaces
with respect to parameter and return types, we need to be cognizant of the fact that
parameters we pass to our public API will ultimately be funnelled through our native
interface. Therefore, we should pay attention to any parameters or return types that
cant be passed directly to a native interface, and start forming a strategy for them. E.g.
consider the following method signature from the Android Mobihelp class:
public static final void showSolutions (Context activityContext,
ArrayList<String> tags)
Weve already decided to just omit the Context parameter in our API, so thats a
non-issue. But what about the ArrayList<String> tags parameter? Passing this
to our public API is no problem, but when we implement the public API, how will we
pass this ArrayList to our native interface, since native interfaces dont allow us to
arrays of strings as parameters?
I generally use one of three strategies in such cases:
1. Encode the parameter as either a single String (e.g. using JSON or some other
easily parseable format) or a byte[] array (in some known format that can easily be
parsed in native code).
2. Store the parameter on the Codename One side and pass some ID or token that
can be used on the native side to retrieve the value.
3. If the data structure can be expressed as a finite number of primitive values,
then simply design the native interface method to take the individual values
41
as parameters instead of a single object. E.g. If there is a User
class with
40
41
http://developer.android.com/reference/android/content/Context.html
https://www.codenameone.com/javadoc/com/codename1/facebook/User.html
642
Callbacks
It is quite often the case that native code needs to call back into Codename One code
when an event occurs. This may be connected directly to an API method call (e.g.
as the result of an asynchronous method invocation), or due to something initiated by
the operating system or the native SDK on its own (e.g. a push notification, a location
event, etc..).
Native code will have access to both the Codename One API and any native APIs in
your app, but on some platforms, accessing the Codename One API may be a little
tricky. E.g. on iOS youll be calling from Objective-C back into Java which requires
knowledge of Codename Ones java-to-objective C conversion process. In general, I
have found that the easiest way to facilitate callbacks is to provide abstractions that
involve static java methods (in Codename One space) that accept and return primitive
types.
In the case of our Mobihelp class, the following method hints at the need to have
a "callback plan":
public static final void getUnreadCountAsync (Context context,
UnreadUpdatesCallback callback)
I.e. If we were to implement this method (which I plan to do), we need to have a way for
the native code to call the callback.onResult() method of the passed parameter.
643
HashMap<Integer,UnreadUpdatesCallback>();
callback) {
callbacks.put(nextId, callback);
}
return nextId++;
callbacks.remove(callbackId);
Display.getInstance().callSerially(new Runnable() {
public void run() {
MobihelpCallbackStatus status2 =
MobihelpCallbackStatus.values()[status];
cb.onResult(status2, count);
}
}
42
});
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/src/com/codename1/
freshdesk/MobihelpNativeCallback.java
644
Initialization
Most Native SDKs include some sort of initialization method where you pass your
developer and application credentials to the API. When I filled in FreshDesks webbased form to create a new application, it generated an application ID, an app "secret",
and a "domain". The SDK requires me to pass all three of these values to its init()
method via the MobihelpConfig class.
Note, however, that FreshDesk (and most other service provides that have native
SDKs) requires me to create different Apps for each platform. This means that my App
ID and App secret will be different on iOS than they will be on Android.
Therefore our public API needs to enable us to provide multiple credentials in the same
app, and our API needs to know to use the correct credentials depending on the device
that the app is running on.
There are many solutions to this problem, but the one I chose was to provide two
different init() methods:
645
and
public final static void initAndroid(MobihelpConfig config)
config.setAppSecret("xxxxxxx");
config.setAppId("freshdeskdemo-2-xxxxxx");
config.setDomain("codenameonetest1.freshdesk.com");
Mobihelp.initIOS(config);
config = new MobihelpConfig();
config.setAppSecret("yyyyyyyy");
config.setAppId("freshdeskdemo-1-yyyyyyyy");
config.setDomain("https://codenameonetest1.freshdesk.com");
Mobihelp.initAndroid(config);
{',','|','/','@','#','%','!','^','&','*','=','+','*','<'};
private static MobihelpNative peer;
....
....
tickets.
...
646
...
...
...
//Retrieve the number of unread items across all the conversations for
callback) {
...
}
configuration.
...
...
tickets.
647
...
...
...
//Display the App Rating dialog with option to Rate, Leave feedback
etc
...
application
...
application.
{
...
application.
...
where only solutions tagged with the given tags are displayed.
...
648
//Search solutions,
...
...
//Search solutions,
//
...
649
/**
* @param domain the domain to set
*/
public void config_setDomain(String domain) ;
/**
* @return the feedbackType
*/
public int config_getFeedbackType() ;
/**
* @param feedbackType the feedbackType to set
*/
public void config_setFeedbackType(int feedbackType);
/**
* @return the launchCountForReviewPrompt
*/
public int config_getLaunchCountForReviewPrompt() ;
/**
* @param launchCountForReviewPrompt the launchCountForReviewPrompt to
set
*/
public void config_setLaunchCountForReviewPrompt(int
launchCountForReviewPrompt);
/**
* @return the prefetchSolutions
*/
/**
* @param prefetchSolutions the prefetchOptions to set
*/
public void config_setPrefetchSolutions(boolean prefetchSolutions);
/**
* @return the autoReplyEnabled
650
enhancedPrivacyModeEnabled) ;
tickets.
//Retrieve the number of unread items across all the conversations for
651
tickets.
public
//Display the App Rating dialog with option to Rate, Leave feedback
etc
application
application.
application.
where only solutions tagged with the given tags are displayed.
//Search solutions,
//Search solutions,
//
652
Notice also, that the native interface includes a set of methods with names prefixed with
config__ . This is just a naming conventions I used to identify methods that map to
the MobihelpConfig class. I could have used a separate native interface for these,
but decided to keep all the native stuff in one class for simplicity and maintainability.
//...
Well initialize this peer inside the init() method of the Mobihelp class. Notice,
though that init() is private since we have provided abstractions for the Android
and iOS apps separately:
//Initialize the Mobihelp support section with necessary app
configuration.
if ("and".equals(Display.getInstance().getPlatformName())) {
init(config);
if ("ios".equals(Display.getInstance().getPlatformName())) {
init(config);
peer = (MobihelpNative)NativeLookup.create(MobihelpNative.class);
peer.config_setAppId(config.getAppId());
653
peer.config_setAutoReplyEnabled(config.isAutoReplyEnabled());
peer.config_setDomain(config.getDomain());
peer.config_setEnhancedPrivacyModeEnabled(config.isEnhancedPrivacyModeEnabled());
if (config.getFeedbackType() != null) {
peer.config_setFeedbackType(config.getFeedbackType().ordinal());
}
peer.config_setLaunchCountForReviewPrompt(config.getLaunchCountForReviewPrompt());
peer.config_setPrefetchSolutions(config.isPrefetchSolutions());
peer.initNative();
}
Things to Notice:
1. The initAndroid() and initIOS() methods include a check to see if they
are running on the correct platform. Ultimately they both call init() .
2. The init() method, uses the NativeLookup
interface.
43
peer.setUserFullName(name);
For some other methods, the public API needs to break apart the parameters into a
form that the native interface can accept. E.g. the init() method, shown above,
takes a MobihelpConfig object as a parameter, but it passed the properties of the
config object individually into the native interface.
Another example, is the showSupport(ArrayList<String> tags) method.
The corresponding native interface method that is wraps is showSupport(String
43
https://www.codenameone.com/javadoc/com/codename1/system/NativeLookup.html
654
sb.append(tag).append(separator);
}
peer.showSupportWithTags(sb.toString().substring(0, sb.length()separator.length()), separator);
}
callback) {
int callbackId =
MobihelpNativeCallback.registerUnreadUpdatesCallback(callback);
peer.getUnreadCountAsync(callbackId);
}
655
44
Some highlights:
1. Context : The native API requires us to pass a context object as a parameter on
many methods. This should be the context for the current activity. It will allow the
FreshDesk API to know where to return to after it has done its thing. Codename One
provides a class called AndroidNativeUtil that allows us to retrieve the apps
Activity (which includes the Context). Well wrap this with a convenience method
in our class as follows:
private static Context context() {
return
com.codename1.impl.android.AndroidNativeUtil.getActivity().getApplicationContext();
This will enable us to easily wrap the freshdesk native API. E.g.:
public void clearUserData() {
}
com.freshdesk.mobihelp.Mobihelp.clearUserData(context());
2. runOnUiThread() - Many of the calls to the FreshDesk API may have been
made from the Codename One EDT. However, Android has its own event
dispatch thread that should be used for interacting with native Android UI.
Therefore, any API calls that look like they initiate some sort of native Android UI
process should be wrapped inside Androids runOnUiThread() method which
is similar to Codename Ones Display.callSerially() method. E.g. see the
showSolutions() method:
public void showSolutions() {
activity().runOnUiThread(new Runnable() {
public void run() {
44
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/native/android/com/
codename1/freshdesk/MobihelpNativeImpl.java
657
com.freshdesk.mobihelp.Mobihelp.showSolutions(context());
}
});
}
com.freshdesk.mobihelp.Mobihelp.getUnreadCountAsync(context(), new
com.freshdesk.mobihelp.UnreadUpdatesCallback() {
public void
Dependencies
Unfortunately, in this case theres a catch. The Mobihelp SDK includes a dependency:
Mobihelp SDK depends on AppCompat-v7 (Revision 19.0+) Library. You
will need to update project.properties to point to the Appcompat library.
If we look inside the project.properties file (inside the Mobihelp SDK directory--i.e. youd need to extract it from the zip to view its contents), youll see the dependency
listed:
android.library.reference.1=../appcompat_v7
I.e. it is expecting to find the appcompat_v7 library located in the same parent
directory as the Mobihelp SDK project. After a little bit of research (if youre not yet
familiar with the Android AppCompat support library), we find that the AppCompat_v7
library is part of the Android Support library, which can can installed into your local
46
Android SDK using Android SDK Manager. Installation processed specified here .
After installing the support library, you need to retrieve it from your Android SDK.
You can find that .aar file inside the ANDROID_HOME/sdk/extras/android/
m2repository/com/android/support/appcompat-v7/19.1.0/ directory (for
version 19.1.0). The contents of that directory on my system are:
45
46
https://s3.amazonaws.com/assets.mobihelp.freshpo.com/sdk/mobihelp_sdk_android.zip
https://developer.android.com/tools/support-library/setup.html
659
appcompat-v7-19.1.0.aar appcompat-v7-19.1.0.pom
appcompat-v7-19.1.0.aar.md5 appcompat-v7-19.1.0.pom.md5
appcompat-v7-19.1.0.aar.sha1 appcompat-v7-19.1.0.pom.sha1
i.e. We need to include the support-v4 library version 19.1.0 in our project.
This is also part of the Android Support library. If we back up a couple of
directories to: ANDROID_HOME/sdk/extras/android/m2repository/com/
android/support , well see it listed there:
appcompat-v7
palette-v7
cardview-v7
recyclerview-v7
gridlayout-v7
support-annotations
leanback-v17
support-v13
mediarouter-v7
support-v4
multidex
test
multidex-instrumentation
660
Looks like this library is pure Java classes, so we only need to include the
support-v4-19.1.0.jar file into our project. Checking the .pom file we see
that there are no additional dependencies we need to add.
So, to summarize our findings, we need to include the following files in our native/
android directory:
1. appcompat-v7-19.1.0.aar
2. support-v4-19.1.0.jar
And since our Mobihelp SDK lists the appcompat_v7 dependency path as "../
appcompat_v7" in its project.properties file, we are going to rename appcompatv7-19.1.0.aar to appcompat_v7.aar .
When all is said and done, our native/android directory should contain the
following:
appcompat_v7.aar mobihelp.andlib
com
support-v4-19.1.0.jar
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.freshdesk.mobihelp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
661
android:name="com.freshdesk.mobihelp.activity.FeedbackActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/Theme.Mobihelp"
android:windowSoftInputMode="adjustResize|stateVisible" >
</activity>
<activity
android:name="com.freshdesk.mobihelp.activity.InterstitialActivity"
android:configChanges="orientation|screenSize"
android:theme="@style/Theme.AppCompat">
</activity>
<activity
android:name="com.freshdesk.mobihelp.activity.TicketListActivity"
android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivit
android:theme="@style/Theme.Mobihelp" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
/>
</activity>
<activity
662
android:name="com.freshdesk.mobihelp.activity.SolutionArticleActivity"
android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivit
android:theme="@style/Theme.Mobihelp"
android:configChanges="orientation|screenSize|keyboard|
keyboardHidden" >
android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
/>
</activity>
<activity
android:name="com.freshdesk.mobihelp.activity.ConversationActivity"
android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivit
android:theme="@style/Theme.Mobihelp"
android:windowSoftInputMode="adjustResize|stateHidden" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
/>
</activity>
<activity
android:name="com.freshdesk.mobihelp.activity.AttachmentHandlerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:parentActivityName="com.freshdesk.mobihelp.activity.SolutionArticleListActivit
android:theme="@style/Theme.Mobihelp" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.freshdesk.mobihelp.activity.SolutionArticleListActivity"
/>
</activity>
663
<service android:name="com.freshdesk.mobihelp.service.MobihelpService" /
>
<receiver android:name="com.freshdesk.mobihelp.receiver.ConnectivityReceiver"
>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>
Well need to add the <uses-permission> tags and all of the contents of the
<application> tag to our manifest file. Codename One provides the following build
hints for these:
1. android.xpermissions - For your <uses-permission> directives. Add a
build hint with name android.xpermissions , and for the value, paste the
actual <uses-permission> XML tag.
2. android.xapplication - For the contents of your <application> tag.
Proguard Config
For the release build, well also need to inject some proguard configuration so that
important classes dont get stripped out at build time. The FreshDesk SDK instructions
state:
If you use Proguard, please make sure you have the following included
in your projects proguard-project.txt
-keep class android.support.v4.** { *; }
-keep class android.support.v7.** { *; }
In addition, if you look at the proguard-project.txt file inside the Mobihelp SDK,
youll see the rules:
-keep public class * extends android.app.Service
664
com.freshdesk.mobihelp.exception.MobihelpComponentNotFoundException
paste
them
into
the
build
hint
47
2. com_codename1_freshdesk_MobihelpNativeImpl.m
48
We make use of the API docs to see how the native SDK needs to be wrapped. The
method names arent the same. E.g. instead of a method showFeedback() , it has
a message -presentFeedback:
47
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/native/ios/
com_codename1_freshdesk_MobihelpNativeImpl.h
48
https://github.com/shannah/cn1-freshdesk/blob/master/cn1-freshdesk-demo/native/ios/
com_codename1_freshdesk_MobihelpNativeImpl.m
49
http://developer.freshdesk.com/mobihelp/ios/api/
665
50
2. Similar to our use of runOnUiThread() in Android, we will wrap all of our API
calls in either dispatch_async() or dispatch_sync() calls to ensure that
we are interacting with the Mobihelp API on the apps main thread rather than the
Codename One EDT.
3. Some methods/messages in the Mobihelp SDK require us to pass a
UIViewController as a parameter. In Codename One, the entire application
uses a single UIViewController: CodenameOne_GLViewController . You can
obtain a reference to this using the [CodenameOne_GLViewController
instance] message. We need to import its header file:
#import "CodenameOne_GLViewController.h"
http://developer.freshdesk.com/mobihelp/ios/integration_guide/#getting-started
666
count){
com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(
param, 3 /*SUCCESS*/, count);
}];
});
In
our
case
the
iOS
SDK
version
of
this
method
is
+unreadCountWithCompletion: which takes a block (which is like an anonymous
function) as a parameter.
The callback to our Codename One function occurs on this line:
com_codename1_freshdesk_MobihelpNativeCallback_fireUnreadUpdatesCallback___int_int_int(C
param, 3 /*SUCCESS*/, count);
1. The
method
name
is
the
result
of
taking
the
FQN
( com.codename1.freshdesk.MobihelpNativeCallback.fireUpdateUnreadUpdates
int, int) ) and replacing all . characters with underscores, suffixing two
underscores after the end of the method name, then appending _int once for
each of the int arguments.
2. We also need to import the header file for this class:
#import "com_codename1_freshdesk_MobihelpNativeCallback.h"
14.14.3.Troubleshooting iOS
If you run into problems with the build, you can select "Include Sources" in the build
server to download the resulting Xcode Project. You can then debug the Xcode project
locally, make changes to your iOS native implementation files, and copy them back into
your project once it is building properly.
51
https://s3.amazonaws.com/assets.mobihelp.freshpo.com/sdk/mobihelp_sdk_ios.zip
668
projects
projects
52
53
When we build the layout we need to take margin into consideration and make sure to
add it into the position/size calculations. Building a layout manager involves two simple
methods: layoutContainer & getPreferredSize .
layoutContainer is invoked whenever Codename One decides the container
needs rearranging, Codename One tries to avoid calling this method and only invokes
it at the last possible moment. Since this method is generally very expensive (imagine
the recursion with nested layouts). Codename One just marks a flag indicating layout
is "dirty" when something important changes and tries to avoid "reflows".
getPreferredSize allows the layout to determine the size desired for the container.
This might be a difficult call to make for some layout managers that try to provide both
flexibility and simplicity.
Most of FlowLayout bugs stem from the fact that this method is just impossible to
implement correctly & efficiently for all the use cases of a deeply nested FlowLayout .
The size of the final layout wont necessarily match the requested size (it probably
wont) but the requested size is taken into consideration, especially when scrolling and
also when sizing parent containers.
This is a layout manager that just arranges components in a center column aligned to
the middle. We then show the proper usage of margin to create a stair like effect with
this layout manager:
class CenterLayout extends Layout {
52
53
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/Layout.html
https://www.codenameone.com/javadoc/com/codename1/ui/Container.html
670
parentStyle.getMargin(Component.LEFT);
int y = parentStyle.getMargin(Component.TOP);
boolean rtl = parent.isRTL();
parentStyle.getMargin(Component.BOTTOM);
parentStyle.getMargin(Component.LEFT);
int width = marginX;
currentStyle.getMargin(Component.RIGHT)
+ currentStyle.getMargin(Component.LEFT), width);
height += currentStyle.getMargin(Component.TOP) +
d.getHeight()
+ currentStyle.getMargin(Component.BOTTOM);
}
Dimension size = new Dimension(width, height);
return size;
671
l.getUnselectedStyle().setMarginLeft(iter * 3);
l.getUnselectedStyle().setMarginRight(0);
}
hi.add(l);
672
The GridBagLayout was ported to Codename One relatively easily considering the
complexity of that specific layout manager. Here are some tips you should take into
account when porting a Swing/AWT layout manager:
1. Codename One doesnt have Insets , we added some support for them in order
to port GridBag but components in Codename One have a margin they need to
consider instead of the Insets (the padding is in the preferred size and is thus
hidden from the layout manager).
2. AWT layout managers also synchronize a lot on the AWT thread. This is no longer
necessary since Codename One is single threaded, like Swing.
3. AWT considers the top left position of the Container to be 0,0 whereas
Codename One considers the position based on its parent Container . The top
left position in Codename One is getX() , getY() .
Other than those things its mostly just fixing method and import statements, which are
slightly different. Pretty trivial stuff.
54
https://www.codenameone.com/javadoc/com/codename1/ui/layouts/GridBagLayout.html
673
15.1.1.What Is A Certificate?
Certificates use cryptographic principals to "sign" data (e.g. an application). Think of
them as you would think of a company stamp, you use them to sign an app so users
know who its from.
15.1.2.What Is Provisioning?
Provisioning provides the hints/guidelines for the application install. E.g. if an
application needs some service from the OS such as push it can usually request that
with provisioning.
In iOS provisioning is separate from the app and you need to also define the devices
supported by the app during development.
15.1.4.What is UDID?
The UDID is the Universal Device Identifier. It identifies mobile devices uniquely,
notice that some operating systems e.g. iOS block access to this value due to privacy
concerns.
You need the iOS device UDID value during development to add the device into the
list of allowed devices.
Dont use an app to get the UDID!
Most return the wrong value, the official way to get the UDID is
thru itunes. We can also recommend trying http://get.udid.io/ which
seems to work rather well
675
676
15.2.2.Selecting Devices
Once you are logged in you will be shown a list of all of the devices that you currently
have registered on your Apple developer account.
677
678
679
680
http://stackoverflow.com/questions/6320255/if-i-revoke-an-existing-distribution-certificate-will-it-mess-up-
anything-with
681
682
683
684
685
686
688
http://developer.apple.com/
https://developer.apple.com/ios/manage/overview/index.action
689
690
691
692
693
694
696
697
698
699
15.5.Android
Signing Android applications is trivial when compared to the pain of iOS signing.
The NetBeans and Eclipse plugins have a simple wizard to generate the certificate that
you can launch by pressing this button:
700
701
Executing the command will produce a Keystore.ks file in that directory which you need
to keep since if you lose it you will no longer be able to upgrade your applications! Fill
in the appropriate details in the project properties or in the CodenameOne section in
the Netbeans preferences dialog.
For more details see http://developer.android.com/guide/publishing/app-signing.html
15.6.RIM/BlackBerry
4
You can now get signing keys for free from Blackberry by going here . Once you
obtain the certificates you need to install them on your machine (you will need the
Blackberry development environment for this). You will have two files: sigtool.db
and sigtool.csk on your machine (within the JDE directory hierarchy). We need
them and their associated password to perform the signed build for Blackberry
application.
15.7.J2ME
Currently signing J2ME applications isnt supported. You can use tools such as the
Sprint WTK to sign the resulting jad / jar produced by Codename One.
https://www.blackberry.com/SignedKeys/
702
File Name
Devices
320x480
Default.png
iPhone 3gs
640x960
iPhone 4x
640x1136
[email protected] iPhone 5x
1024x768
DefaultPortrait.png
768x1024
DefaultLandscape.png
Non-retina ipads in
landscape mode
Apple provided another trick with XIB files starting with iOS 8 but that doesnt apply to games or
Codename One. It has its own set of problems
2
slightly larger screen and different aspect ratio
703
File Name
Devices
2048x1536
1536x2048
750x1334
[email protected] iPhone 6
1242x2208
2208x1242
16.1.1.Size
One of the first things we ran into when building one of our demos was a case where
an app that wasnt very big in terms of functionality took up 30mb!
704
16.1.3.Unsupported component
One of the biggest obstacles is with heavyweight components, e.g. if you use a
browser or maps on the first screen of the app you will see a partially loaded/distorted
3
MapComponent and the native webkit browser obviously cant be rendered properly
by our servers.
The workaround for such issues is to have a splash screen that doesnt include any
of the above. Its OK to show it for a very brief amount of time since the screenshot
process is pretty fast.
16.2.1.Sending Notifications
The process for sending a notification is:
1. Create a LocalNotification
notification.
3
4
https://www.codenameone.com/javadoc/com/codename1/maps/MapComponent.html
https://www.codenameone.com/javadoc/com/codename1/notifications/LocalNotification.html
705
n.setId("demo-notification");
n.setAlertBody("It's time to take a break and look at me");
n.setAlertTitle("Break Time!");
n.setAlertSound("beep-01a.mp3");
Display.getInstance().scheduleLocalNotification(
n,
frequency
);
706
16.2.2.Receiving Notifications
The API for receiving/handling local notifications is also similar to
push. Your applications main lifecycle class needs to implement the
com.codename1.notifications.LocalNotificationCallback
interface
which includes a single method:
707
The notificationId parameter will match the id value of the notification as set
using LocalNotification.setId() .
//...
}
public void stop() {
}
//...
//...
16.2.3.Canceling Notifications
Repeating notifications will continue until they are canceled by the app. You can cancel
a single notification by calling:
Display.getInstance().cancelLocalNotification(notificationId);
708
16.3.Push Notifications
Push notification allows us to send a notification to a device while the application
might be in the background. This is important both as a marketing tool and as a basic
communications device.
16.3.1.What Is Push?
If you are new to mobile development then you heard a lot of buzzwords and very little
substance. The problem is that iOS and Android have very different ideas of what push
is and should be.
For Android push is a communication system that the server can initiate. E.g. the cloud
can send any packet of data and the device can process it in rather elaborate ways.
For iOS push is mostly a visual notification triggered by the server to draw attention to
new information inside an app.
709
710
Figure 16.4. Enter the details for the app and the package
Click the Cloud Messaging option then click the Enable Google Cloud Messaging button
712
Figure 16.5. Click the "Cloud Messaging" option then click the
"Enable Google Cloud Messaging" button
You should now have the values for both
GCM_SENDER_ID
&
GCM_SERVER_API_KEY as illustrated below. Notice that the sender id is the numeric
key whereas the api key is the alpha-numeric key:
713
Figure 16.6. You should now have the values for both
GCM_SENDER_ID & GCM_SERVER_API_KEY
714
715
iOS Push certificates have been created for your app with bundle ID
com.mycompany.myapp.mypushdemo. Please file this email away for safe
keeping as you will need the details about the certificate locations and
passwords to use Push successfully in your apps.
The URLs and passwords are everything that you will need later on to get push working
on iOS. Notice that the wizard also performs a couple of other tasks specifically it sets
the ios.includePush build hint to true & adds push to the provisioning profile etc.
You can read more about the certificate wizard in the signing section
theme = UIManager.initFirstTheme("/theme");
716
if(current != null){
current.show();
return;
metaData.put(com.codename1.push.Push.GOOGLE_PUSH_KEY,
GCM_SENDER_ID);
Display.getInstance().registerPush(metaData, true);
});
hi.show();
}
public void stop() {
current = Display.getInstance().getCurrent();
if(current instanceof Dialog) {
((Dialog)current).dispose();
current = Display.getInstance().getCurrent();
@Override
@Override
con.addArgument("pushKey", Push.getPushKey());
NetworkManager.getInstance().addToQueue(con);
@Override
717
A client must implement the PushCallback interface in the main class of the
application (the one with the callback methods)!
This sample requires a server that will accept the device id. Push is challenging
without some central repository that will collect device IDs from the clients
You get the GCM_SENDER_ID value from Google as discussed above
We register push every time we start the application, this is expected behavior
since push details might change.
Notice that we wrap the call in a callSerially , this provides the first Form
with time to appear. This is important as the user can be prompted for permissions
at which point you wouldnt want that prompt to show on top of a blank screen.
This method is invoked when a push message is received, it can be invoked
multiple times for some cases
After registration push might still not be valid, it is only valid once this method is
invoked. While its not essential to register in the server at this point, its probably
what you should do.
The push token is a unique "key" that you can use to send push thru your Codename
One account. It allows you to send push messages without placing your Codename
One email or password into your source files.
You can get it by going to the Codename One build server dashboard at https://
www.codenameone.com/build-server.html and selecting the Account tab.
The token should appear at the bottom as such:
https://www.codenameone.com/javadoc/com/codename1/push/PushCallback.html
718
When sending push to iOS devices we have two modes: - Production - Distribution
This allows you to debug the push related functionality without risking the possibility
of sending a push into a production app. Its important to send the values to the right
server during development/production.
private static final boolean ITUNES_PRODUCTION_PUSH = false;
iOS needs a certificate in order to send a push, this allows you to prove to Apples push
servers that you are who you claim to be (the author of the app).
719
To simplify these use cases we added the Push API. To use the Push API you need
the device key of the destination device to which you want to send the message. You
can get that value from the Push.getPushKey() method. Notice that you need that
value from the destination device and not the local device!
To send a message to another device just use:
String cert = ITUNES_DEVELOPMENT_PUSH_CERT;
String pass = ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD;
if(ITUNES_PRODUCTION_PUSH) {
cert = ITUNES_PRODUCTION_PUSH_CERT;
pass = ITUNES_PRODUCTION_PUSH_CERT_PASSWORD;
}
Push.sendPushMessage(PUSH_TOKEN, "Hello World",
ITUNES_PRODUCTION_PUSH, GCM_SERVER_API_KEY, cert, pass, 1,
deviceKey));
https://www.codenameone.com/javadoc/com/codename1/push/Push.html
720
in
or
cert - http or https URL containing the push certificate for an iOS push ITUNES_DEVELOPMENT_PUSH_CERT or ITUNES_PRODUCTION_PUSH_CERT
We can thus send a push from Java EE using code like this:
URLConnection connection = new URL("https://push.codenameone.com/push/
push").openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-formurlencoded;charset=UTF-8");
String cert = ITUNES_DEVELOPMENT_PUSH_CERT;
String pass = ITUNES_DEVELOPMENT_PUSH_CERT_PASSWORD;
721
cert = ITUNES_PRODUCTION_PUSH_CERT;
pass = ITUNES_PRODUCTION_PUSH_CERT_PASSWORD;
}
String query = "token=" + PUSH_TOKEN +
"&device=" + deviceId1 +
"&device=" + deviceId2 +
"&device=" + deviceId3 +
"&type=1" +
"&auth=" + GCM_SERVER_API_KEY +
"&certPassword=" + URLEncoder.encode(pass, "UTF-8") +
"&cert=" + cert +
"&body=" + URLEncoder.encode(MESSAGE_BODY, "UTF-8") +
"&production=" + ITUNES_PRODUCTION_PUSH;
output.write(query.getBytes("UTF-8"));
int c = connection.getResponseCode();
// read response JSON
Notice that you can send a push to 500 devices. To send in larger batches you need
to split the push requests into 500 device batches.
722
Badging on iOS
The badge number can be set thru code as well, this is useful if you want the
badge to represent the unread count within your application.
7
To take advantage of that capability use the build hint ios.testFlight=true and
then submit the app to the store for beta testing. Make sure to use a release build target.
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
724
However, it seems that Apple will reject your app if you just include that and dont have
a good reason.
16.6.Using Cocoapods
8
10
For full versioning syntax specifying pods see the Podfile spec for the "pod" directive
11
725
ios.pods=GoogleMaps,AFNetworking
Or specifying versions:
ios.pods=AFNetworking ~> 3.0,GoogleMaps
This would translate to the following build hints in your Codename One project:
ios.pods.sources=https://github.com/CocoaPods/Specs.git
ios.pods.platform=7.0
ios.pods=GoogleMaps
726
Codename Ones Javascript port uses TeaVM to compile your application directly
to Javascript so that it can run inside modern web browsers without the need for
any plugins (i.e. NOT as an applet). One of the revolutionary features that TeaVM
provides is the ability to run multi-threaded code (i.e. it has full support for Object.wait(),
Object.notify(), Object.notifyAll(), and the synchronized keyword). The one caveat
to be aware of is that you cannot use any threading primitives inside static
initializers. This is due to technical limitations in the browser environment and the way
that TeaVM compiles class definitions into Javascript. The workaround for this issue is
to do lazy initialization in cases where you need to use multithreaded code.
Example
The following code will result in a build error when deploying a Javascript build:
Class1.java
import com.codename1.io.Log;
class Class1 {
}
1
2
return 1;
https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html
http://teavm.org/
727
This fails because Class2 calls Class1.getValue() in its static initializer, and
getValue() calls Log.p() , which, underneath the covers, writes to Storage which involves some synchronous network access in the Javascript port (i.e. it uses
wait() and notify() under the hood.
If we simply remove the call to Log.p() from getValue() , as follows:
public static int getValue() {
}
return 1;
META-INF/
META-INF/MANIFEST.MF
assets/
assets/META-INF/
js/
https://github.com/codenameone/CodenameOne/issues
729
0 Thu
0 Thu
corsproxy/
0 Thu
27568 Thu
306312 Thu
427737 Thu
350 Thu
92671 Thu
23549 Thu
2976 Thu
30695 Thu
84319 Thu
30
30
30
30
30
30
30
30
30
30
15:57:36
15:57:12
15:57:12
15:57:12
15:57:12
15:57:12
15:57:14
15:57:14
15:57:12
15:57:12
PDT
PDT
PDT
PDT
PDT
PDT
PDT
PDT
PDT
PDT
2015
2015
2015
2015
2015
2015
2015
2015
2015
2015
WEB-INF/lib/
assets/CN1Resource.res
assets/iOS7Theme.res
assets/iPhoneTheme.res
assets/META-INF/MANIFEST.MF
assets/theme.res
icon.png
index.html
js/fontmetrics.js
js/jquery.min.js
731
Time
---15:57
CRC-32
-----9dc91739
Name
---assets/
15:57
0b5c1c3a
assets/
15:57
3de499c8
assets/
15:57
7e7e3714
assets/META-INF/
15:57
15:57
15:57
15:57
004ad9d7
acd79066
e5341de1
2e008f6c
assets/theme.res
icon.png
index.html
js/
15:57
15:57
15:57
15:57
15:57
15b91689
51b895c7
a12159c7
2b34c50f
30abdf13
js/jquery.min.js
progress.gif
style.css
teavm/classes.js
teavm/
15:57
e5c456f7
teavm/
15:57
46651f06
teavm/runtime.js
------15 files
The Codename One API includes a network layer (the NetworkManager and
5
ConnectionRequest classes) that allows you to make HTTP requests to arbitrary
destinations. When an application is running inside a browser as a Javascript app, it
is constrained by the same origin policy. You can only make network requests to the
same host that served the app originally.
E.g. If your application is hosted at http://example.com/myapp/index.html, then your
app will be able to perform network requests to retrieve other resources under the
example.com domain, but it wont be able to retrieve resources from example2.com,
foo.net, etc..
The HTTP standard does support cross-origin requests in
the browser via the Access-Control-Allow-Origin HTTP
header. Some web services supply this header when serving
resources, but not all. The only way to be make network requests to
arbitrary resources is to do it through a proxy.
Luckily there is a solution. The .war javascript distribution includes an embedded proxy
servlet, and your application is configured, by default, to use this servlet. If you intend to
use the .war distribution, then it should just work. You shouldnt need to do anything
to configure the proxy.
4
5
https://www.codenameone.com/javadoc/com/codename1/io/NetworkManager.html
https://www.codenameone.com/javadoc/com/codename1/io/ConnectionRequest.html
732
The easiest way to set up a proxy is to use the Codename One cors-proxy project.
This is the open-source project from which the proxy in the .war distribution is derived.
Simply download and install the cors-proxy .war file in your JavaEE compatible servlet
container.
If you dont want to install the .war file, but would rather just copy the proxy servlet
into an existing web project, you can do that also. See the cors-proxy wiki for more
7
information about this .
https://github.com/shannah/cors-proxy
https://github.com/shannah/cors-proxy/wiki/Embedding-Servlet-into-Existing-Project
733
Display.getInstance().setProperty(
"javascript.proxy.url",
"http://example.com/myapp/cn1-cors-proxy?_target="
);
The method you choose will depend on the workflow that you prefer. Options #1 and
#3 will almost always result in fewer changes than #2 because you only have to set
them up once, and the builds will retain the settings each time you build your project.
17.6.Debugging
If you run into problems with your app that only occur in the Javascript version, you may
need to do a little bit of debugging. There are many debugging tools for Javascript, but
the preferred tool for debugging Codename One apps is Chromes debugger.
If your application crashes and you dont have a clue where to begin, follow these steps:
1. Load your application in Chrome.
2. Open the Chrome debugger.
3. Enable the "Pause on Exceptions" feature, then click the "Refresh" button to reload
your app.
4. Step through each exception until you reach the one you are interested in. Chrome
will then show you a stack trace that includes the name of the Java source file and
line numbers.
734
735
17.7.1.Libraries vs Resources
A resource is a file whose contents can be loaded by your application at runtime
using Display.getInstance().getResourceAsStream() . In a typical Java
environment, resources would be stored on the applications classpath (usually
inside a Jar file). On iOS, resources are packaged inside the application bundle. In
the Javascript port, resources are stored inside the APP_ROOT/assets directory.
Historically, javascript files have always been treated as resources in Codename
One, and many apps include HTML and Javascript files for use inside the
8
BrowserComponent .
With the Javascript port, it isnt quite so clear whether a Javascript file is meant to be
a resource or a library that the application itself uses. Most of the time you probably
want Javascript files to be used as libraries, but you might also have Javascript files
in your app that are meant to be loaded at runtime and displayed inside a Web View
- these would be considered resources.
https://www.codenameone.com/javadoc/com/codename1/ui/BrowserComponent.html
736
"javascript" : {
"libs" : [
"mylib1.js"
I.e. It contains a object with key libs whose value is a list of files that should be treated as
libraries. In the above example, we are declaring that the file native/javascript/
mylib1.js should be treated as a library. This will result in the following <script>
tag being added to the index.html file:
<script src="includes/mylib1.js"></script>
"javascript" : {
"libs" : [
"mylib1.js",
{
"file" : "mylib2.js",
"include" : false
}
]
}
737
Library Directories
You can also specify directories in the manifest file. In this case, the entire directory will
be packaged inside the includes directory of your app.
If you are including Javascript files in your app that are contained
inside a directory hierarchy, you should specify the root directory of
the hierarchy in your manifest file and use the sub "includes" property
of the directory entry to specify which files should be included with
<script> tags. Specifying the file directly inside the "libs" list will
result in the file being packed directly in the your apps includes
directory. This may or may not be what you want.
E.g.
{
"javascript" : {
"libs" : [
"mylib1.js",
{
"file" : "mylib2.js",
"include" : false
},
{
"file" : "mydir1",
"includes" : ["subfile1.js", "subfile2.js"]
}
}
In this example the entire mydir1 directory would be packed inside the apps
includes directory, and the following script tags would be inserted into the
index.html file:
<script src="includes/mydir1/subfile1.js"></script>
<script src="includes/mydir1/subfile2.js"></script>
738
"javascript" : {
"libs" : [
"//maps.googleapis.com/maps/api/js?v=3.exp"
]
}
This example uses the "//" prefix for the URL instead of specifying
the protocol directly. This allow the library to work for both http and
https hosting. You could however specify the protocol as well:
+
{
"javascript" : {
"libs" : [
"https://maps.googleapis.com/maps/api/js?v=3.exp"
]
}
739
"javascript" : {
"libs" : [
"mystyles.css"
]
}
or
{
"javascript" : {
"libs" : [
"https://example.com/mystyles.css"
]
}
"javascript" : {
"libs" : [
"//maps.googleapis.com/maps/api/js?
v=3.exp&key={{javascript.googlemaps.key}}"
740
Description
browser.window.location.href
browser.window.location.hash
Description
browser.window.location.pathname
A String, representing the pathname
browser.window.location.protocol
A String, representing the protocol of the
current URL, including the colon (:)
browser.window.location.port
browser.window.location.hostname
A String, representing the domain name,
or the IP address of a URL
User-Agent
browser.language
browser.name
Platform
browser.codeName
browser.version
javascript.deployment.type
in your app. All of the native themes are available on GitHub, so you can easily copy
these into your application. The best place to add the theme is in your native/
javascript directory - so that they wont be included for other platforms.
First, download androidTheme.res from the Android port on GitHub, and copy it into
your apps native/javascript directory.
Then in your apps init() method, add the following:
Display d = Display.getInstance();
d.setProperty("javascript.native.theme", "/androidTheme.res");
https://www.codenameone.com/javadoc/com/codename1/ui/Display.html
10
https://github.com/codenameone/CodenameOne/raw/master/Ports/Android/src/androidTheme.res
743
744
745
746
747
748
2. Select the "Send Windows UWP Build" option in the Codename One menu of your
IDE. This will initiate the build on the Codename One build server.
749
3. Log into the Codename One dashboard to watch the build progress. When it is
complete, youll be able to download the ".appxbundle" file to your desktop.
750
3. If this is the first time installing a UWP (debug) app on your device, you will need
to install the dependencies. You can find the dependencies for mobile/ARM apps
1
here . Youll need to install both Microsoft.NET.CoreRuntime.1.0.appx
and Microsoft.VCLibs.ARM.Debug.14.00.appx . If this is not the first
time installing a UWP app, you can skip to the next step.
a. Under the "Install App" section, click the "Choose File" button and navigate
through the file chooser to select the "Microsoft.NET.CoreRuntime.1.0.appx"
file. Then click "Go".
1
https://github.com/codenameone/cn1-binaries/tree/master/uwp/Dependencies/ARM
751
752
753
754
3. Under "Build Type", make sure that "Desktop Debug Build" is selected, as shown
below:
755
4. Save the changes by clicking the "Disk" icon in the upper right:
Now you can proceed to send the build to the build server.
1. Select the "Send Windows UWP Build" option in the Codename One menu of your
IDE. This will initiate the build on the Codename One build server.
756
757
After extraction, open the resulting directory. You should see contents similar to the
following:
Downloading Dependencies
If this is your first time installing a UWP app on this PC, you may need to add the
2
dependencies before you can install. You can download the dependencies here .
Extract "Dependencies.zip" and copy the resulting "Dependencies" directory into the
app install directory. Your app install directory should now look like:
2
https://github.com/codenameone/cn1-binaries/raw/master/uwp/Dependencies.zip
758
You may be prompted that you need to change the execution policy, in Powershell:
And if you look in your "Windows Menu" under "All Apps", you should see your app
listed there:
760
761
If you dont already have an account, sign up for one. Then log in. Once logged in, you
can click the "Dashboard" link on the toolbar.
https://developer.microsoft.com/en-us/windows/publish
762
Under the "Your apps" section (on the left in the above screenshot), click the "Create
new app" button.
763
764
Then click OK. This will generate a .pfx file inside your project folder.
The "Display Name" must also match that app name in the store.
Finally, make sure that "Windows Store Upload" is selected in the "Build Type" field.
For the example above, my settings form looks like the following screenshot when I
am done.
765
See the Microsofts documentation on uploading app packages for more information
on the remaining steps.
https://msdn.microsoft.com/en-us/windows/uwp/publish/upload-app-packages
766
767
com.codename1.impl.NativeThreadImpl.<>c__DisplayClass6_0.<init>b__0()
at System.Threading.Tasks.Task.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
at System.Threading.Tasks.Task.ExecutionContextCallback(Object obj)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&
currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean
bPreventDoubleExecution)
at
System.Threading.Tasks.ThreadPoolTaskScheduler.LongRunningThreadWork(Object
obj)
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart(Object obj)
Originating from:
Message=Object reference not set to an instance of an object.
at com.propertycross.codename1.PropertyCross.1.run()
at com.codename1.ui.Display.processSerialCalls()
at com.codename1.ui.Display.edtLoopImpl()
at com.codename1.ui.Display.invokeAndBlock(Runnable r, Boolean
dropEvents)
It will show you the call stack with the names of the methods. But it wont show you
the line numbers. If the stack trace isnt specific enough, you can add Log.p()
statements in various positions in my code to help narrow down the source of the
exception.
768
19.1.The Game
We will create a poker game for 1 player that doesnt include the betting process or any
of the complexities such as AI, card evaluation or validation. This allows us to fit the
whole source code in 270 lines of code (more due to comments). I also chose to simplify
the UI for touch devices only, technically it would be pretty easy to add keypad support
but it would complicate the code and require additional designs (for focus states).
You can see the game running on the simulator at http://
www.youtube.com/watch?v=4IQGBT3VsSQ
The game consists of two forms: Splash screen and the main game UI.
769
770
19.1.2.Resources
To save some time/effort we suggest using the ready made resource files linked in the
On The Web section below. I suggest skipping this section and moving on to the code,
however for completeness here is what we did to create these resources:
You will need a gamedata.res file that contains all the 52 cards as multi images using
the naming convention of rank suite.png example: 10c.png (10 of clubs) or ad.png
(Ace of diamonds).
To accomplish this we can create 52 images of roughly 153x217 pixels for all the cards
then use the designer tool and select Quick Add MultiImages from the menu. When
prompted select HD resolution. This effectively created 52 multi-images for all relevant
resolutions.
You can also modify the default theme that ships with the application in small ways
to create the white over green color scheme. Open it in the designer tool by double
clicking it and select the theme.
Then press Add and select the Form entry with background NONE , background color
6600 and transparency 255 .
Add a Label style with transparency 0 and foreground 255 and then copy the style
to pressed/selected (since its applied to buttons too).
Do the same for the SplashTitle / SplashSubtitle but there also set the
alignment to CENTER , the Font to bold and in the case of SplashTitle to Large
Font as well.
771
19.1.4.The Game UI
Initially when entering the game form we have another animation where all the cards
are laid out as you can see in Figure 19.2, Game form startup animation and deal
animation. We then have a long sequence of animation where the cards unify into
place to form a pile (with a cover background falling on top) after which dealing begins
772
https://www.codenameone.com/javadoc/com/codename1/ui/util/UITimer.html
773
in the game
/**
* We use this method to calculate a "fake" DPI based on screen
resolution rather than its actual DPI
* this is useful so we can have large images on a tablet
*/
private int calculateDPI() {
Display.getInstance().getDisplayWidth();
if(pixels > 1000000) {
}
return Display.DENSITY_HD;
return Display.DENSITY_VERY_HIGH;
774
return Display.DENSITY_HIGH;
return Display.DENSITY_MEDIUM;
/**
* This method is invoked by Codename One once when the application
loads
*/
public void init(Object context) {
try{
a resource with
resource rather
UIManager.getInstance().setThemeProps(theme.getTheme(theme.getThemeResourceNames()
[0]));
cards = Resources.open("/gamedata.res", calculateDPI());
} catch(IOException e) {
e.printStackTrace();
/**
* This method is invoked by Codename One once when the application
loads and when it is restarted
*/
public void start() {
if(current != null){
current.show();
return;
}
showSplashScreen();
/**
* The splash screen is relatively bare bones. Its important to have a
splash screen for iOS
* since the build process generates a screenshot of this screen to
speed up perceived performance
*/
public void showSplashScreen() {
775
sides.
border.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE);
splash.setLayout(border);
// by default the form's content pane is scrollable on the Y axis
// we need to disable it here
splash.setScrollable(false);
in the theme
title.setUIID("SplashTitle");
splash.addComponent(BorderLayout.NORTH, title);
splash.addComponent(BorderLayout.SOUTH, subtitle);
Label as = new Label(cards.getImage("as.png"));
Label ah = new Label(cards.getImage("ah.png"));
Label ac = new Label(cards.getImage("ac.png"));
Label ad = new Label(cards.getImage("ad.png"));
// a layered layout places components one on top of the other in
animation
splash.addComponent(BorderLayout.CENTER, center);
splash.show();
splash.setTransitionOutAnimator(CommonTransitions.createCover(CommonTransitions.SLIDE_V
true, 800));
776
Display.getInstance().callSerially(new Runnable() {
public void run() {
center.setLayout(new BoxLayout(BoxLayout.X_AXIS));
center.setShouldCalcPreferredSize(true);
splash.getContentPane().animateHierarchy(2000);
});
showGameUI();
}
}).schedule(2500, false, splash);
/**
* This is the method that shows the game running, it is invoked to
start or restart the game
*/
private void showGameUI() {
deck
ArrayList<Card>(Arrays.asList(deck));
Collections.shuffle(shuffledDeck);
final Form gameForm = new Form();
gameForm.setTransitionOutAnimator(CommonTransitions.createCover(CommonTransitions.SLIDE
true, 800));
Container gameFormBorderLayout = new Container(new
BorderLayout());
777
// we place two layers in the game form, one contains the contents
gameForm.setLayout(new LayeredLayout());
gameForm.addComponent(gameFormBorderLayout);
gameForm.addComponent(gameUpperLayer);
// The game itself is comprised of 3 containers, one for each
// gather into the deck, that is why we set the initial deck
GridLayout(4, 13));
GridLayout(1, 5));
GridLayout(1, 5));
// we place all the card images within the deck container for the
initial animation
Label(cards.getImage(deck[iter].getFileName()));
// containers have no padding or margin this effectively
face.setUIID("Container");
deckContainer.addComponent(face);
// we place our cards at the bottom, the deck at the center and
gameFormBorderLayout.addComponent(BorderLayout.CENTER,
deckContainer);
gameFormBorderLayout.addComponent(BorderLayout.NORTH,
rivalContainer);
gameFormBorderLayout.addComponent(BorderLayout.SOUTH,
playerContainer);
gameForm.show();
778
so later players
Button(cards.getImage("card_back.png"));
cardBack.setDropTarget(true);
// we remove the button styling so it doesn't look like a
cardBack.setUIID("Label");
deckContainer.addComponent(cardBack);
// we set the layout to layered layout which places all
// the layout into place, this will cause the spread out
deckContainer.setLayout(new LayeredLayout());
deckContainer.animateLayoutAndWait(3000);
// Now we iterate over the cards and deal the top card
779
cards.getImage("card_back.png"), currentCard);
}
notice.getUnselectedStyle().setAlignment(Component.CENTER);
gameUpperLayer.addComponent(notice);
gameUpperLayer.layoutContainer();
// we place the notice then remove it without the
temp.setPreferredSize(new Dimension(notice.getWidth(),
notice.getHeight()));
gameUpperLayer.replace(notice, temp, null);
gameUpperLayer.layoutContainer();
gameUpperLayer.replace(temp, notice,
CommonTransitions.createFade(1500));
the game
// when the user taps the card back (the deck) we finish
cardBack.addActionListener(new ActionListener() {
780
play again...
(Button)rivalContainer.getComponentAt(iter);
Label(cards.getImage(currnetCard.getFileName()));
rivalContainer.replaceAndWait(cardButton,
l, CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL,
true, 300));
}
// notice dialogs are blocking by default so its
Display.getInstance().exitApplication();
// play again
});
showGameUI();
}
}).schedule(1800, false, gameForm);
/**
* A blocking method that creates the card deal animation and binds
the drop logic when cards are dropped on the deck
*/
private void dealCard(Component deck, final Container destination,
781
card.setY(deckAbsY - destination.getAbsoluteY());
} else {
card.setY(deckAbsY);
}
card.setWidth(deck.getWidth());
card.setHeight(deck.getHeight());
destination.addComponent(card);
// we save the model data directly into the component so we don't
// need to check the card type a user touched we can just use
getClientProperty
card.putClientProperty("card", currentCard);
destination.getParent().animateHierarchyAndWait(400);
card.setDraggable(true);
// when the user drops a card on a drop target (currently only the
card.addDropListener(new ActionListener() {
});
evt.consume();
card.getParent().removeComponent(card);
destination.animateLayout(300);
current = Display.getInstance().getCurrent();
782
switch(rank) {
case 11:
return "j";
case 12:
return "q";
case 13:
return "k";
case 14:
}
}
return "a";
783
20.1.Basic Concepts
The basic premise is this: the designer creates a UI version and names GUI
components. It can create commands as well, including navigation commands (exit,
minimize). It can also define a long running command, which will, by default, trigger a
thread to execute.
All UI elements can be loaded using the UIBuilder
2
Resources API?
Since the Resources class is essential for using Codename One, adding the
UIBuilder as an import would cause any application (even those that dont use the
UIBuilder) to increase in size! We dont want people who arent using the feature to pay
the penalty for its existence!
The UIBuilder is designed for use as a state machine, carrying out the current state
of the application, so when any event occurs a subclass of the UIBuilder can just
process it. The simplest way and most robust way for changes is to use the Codename
One Designer to generate some code for you (yes, we know its not a code generation
tool, but there is a hack).
When using a GUI builder project, it will constantly regenerate the state machine base
class, which is a UIBuilder subclass containing most of what you need.
The trick is not to touch that code! DO NOT CHANGE THAT CODE!
Sure, you can change it and everything will be just fine, however if you make changes
to the GUI, regenerating that file will obviously lose all those changes, which is not
something you want!
1
2
https://www.codenameone.com/javadoc/com/codename1/ui/util/UIBuilder.html
https://www.codenameone.com/javadoc/com/codename1/ui/util/Resources.html
784
https://www.codenameone.com/javadoc/com/codename1/ui/list/MultiList.html
785
786
https://www.codenameone.com/javadoc/com/codename1/ui/list/ListModel.html
787
788