Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

CodenameOne examples for Nokia X & Asha

From Wiki
Jump to: navigation, search

This article explains how to use CodenameOne to develop applications that run on both Nokia X and Asha devices

Article Metadata
Tested with
SDK: NetBeans IDE 8.0 with CodenameOne plugin
Article
Created: (10 Jul 2014)
Last edited: vihtori (11 Jul 2014)

Contents

Introduction

CodenameOne offers a framework to write your applications in Java and build packages for multiple platforms. This article will use NetBeans 8.0 IDE with the CodenameOne plugin as the development platform. If you are not familiar with how to build applications with CodenameOne, reading Cross-platform development for Nokia X & Asha will get you started.


Summary

This article contains 6 simple examples. Each example describes a typical feature one would need to implement when developing a mobile app. The first two examples show how to handle basic user interaction. The third example downloads and displays RSS feed in a list component. Fourth example introduces animated layout change. The last two examples are about custom drawing and dynamic drawing.

HelloDialog

Start by creating a new project using "Hello World (Manual)" template. The template generates basic "Hello World" application. Open MyApplication.java file from the application package and modify the There is automatically generated start() method for displaying “Hello world” label. Replace the code with the following code.


public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form();
Button btn = new Button("Say hello");
btn.addActionListener(new ActionListener() {
 
public void actionPerformed(ActionEvent evt) {
Dialog.show("This is a dialog", "Hello World!", "Close", null);
}
});
hi.addComponent(btn);
hi.show();
}

Instead of the "Hello World" Label, a new Button is added to the Form. The Button click events are handled by a ActionListener object. The ActionListener displays a Dialog when the Button is clicked. The Dialog can be used to display complex views, but this example uses the Dialog.show() variation, which takes the title, message, close button title and an optional icon.

File:HelloDialog NetBeans 8.zip

HelloDialogVisual

Start by creating a new project using "Hello World (Visual)" template. The template generates a basic "Hello World" application, but uses different approach than the first example. A StateMachine.java file is generated to the userclasses package. This file is used by CodenameOne to bind the components built in CodenameOne Designer to the application code.

Now open the designer by double clicking theme.res. Select “GUI Builder” front the panel on left. Select the automatically generated “Main” Form from the list and remove the default label from the form by right clicking the item and selecting “delete” from the context menu.

CodenameOne Designer

Add button to the form by clicking the Button component from the “Core Components” box. Change the button text to “Say hello” by double-clicking the button on the preview pane. Right click the Button component on the preview pane and select “Events”->"Action Event" to bind button events to the code.

Save the changes you have made in the designer and switch back to Netbeans window. Go to StateMachine.java under user classes package. Notice the onMain_ButtonAction(Component c, ActionEvent event) {…} method, which was generated to handle the button events. If the @Override annotation gives compilation errors, make sure you have saved the changes in the Codename One designer window. Now add the code to display a dialog:

    @Override
protected void onMain_ButtonAction(Component c, ActionEvent event) {
Dialog.show("This is a dialog", "Hello World!", "Close", null);
}


File:HelloDialogVisual NetBeans 8.zip

RSSExample

The third example is a bit more complex. First the application will load an RSS feed xml from the web, then using the feed is parsed using the CodenameOne built in xml library and finally the parsed news items are displayed on a list component. The example is essentially a list of news items so start by creating a new project using the "List Application" template.

Open the designer by double clicking the theme.res. Select the Main form from the left and you will see the list view generated by the "List Application" template. Start by removing the placeholder data from the list. Select the multiList item and click "Properties" tab from the central panel. The list items can be found at the bottom of the properties list.

Remove placeholder data.png

Next open the "Events" tab and click "Post Show" to generate method for running code right after the Main form is displayed.

Add event.png

Save changes in the designer and switch back to the NetBeans window. Open the StateMachine.java file under the userclasses package. protected void postMain(Form f) method is generated there and will be called when the form is displayed. Now lets add the xml loading and parsing to our example.

@Override
protected void postMain(Form f) {
ConnectionRequest request = new ConnectionRequest();
request.setUrl("http://feeds.bbci.co.uk/news/world/rss.xml");
request.setPost(false);
request.addResponseListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
NetworkEvent event = (NetworkEvent)evt;
Exception error = event.getError();
if(error != null) {
return;
}
ConnectionRequest request = event.getConnectionRequest();
ArrayList<RSSItem> items = parseRSSFeed(request.getResponseData());
List list = findMultiList();
for(int i = 0; i < items.size(); i++) {
RSSItem item = items.get(i);
//Create the hashtable to bind item data to the default table cell
Hashtable table = new Hashtable();
table.put("Line1", item.title);
table.put("Line2", item.description);
table.put("Line3", item.date);
if(item.thumbUrl != null) {
//Use the ImageDownloadService to load the image in background and update the list model when done.
ImageDownloadService.createImageToStorage(item.thumbUrl, list, i, "icon", item.thumbUrl, null);
}
list.addItem(table);
}
}
});
NetworkManager.getInstance().addToQueue(request);
}


First a NetworkRequest object is created and pointed to the location of the xml feed. Then an ActionListener is added to the request to handle the response data. Finally the NetworkManager takes care of sending the request. Now the NetworkManager will process the request and take care of the networking. Finally the ActionListener will be notified when the request has a response. The listener receives a NetworkEvent object from which we can get the ConnectionRequest object and also query if there were any errors during the processing of the HTTP request. In this example the listener will simply return in case of errors. The response body can be accessed by calling request.getResponseData(). The data is a byte array and it is simply passed to our xml parsing code.

    static class RSSItem {
public String title;
public String description;
public String date;
public String thumbUrl;
}
 
private ArrayList<RSSItem> parseRSSFeed(byte[] data) {
ArrayList<RSSItem> results = new ArrayList<RSSItem>();
XMLParser parser = new XMLParser();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Element root = parser.parse(new InputStreamReader(bais));
 
Element channel = root.getFirstChildByTagName("channel");
Vector<Element> items = channel.getDescendantsByTagName("item");
for(Element e : items) {
RSSItem item = new RSSItem();
Element title = e.getFirstChildByTagName("title");
if(title != null) {
item.title = title.getChildAt(0).getText();
}
Element desc = e.getFirstChildByTagName("description");
if(desc != null) {
item.description = desc.getChildAt(0).getText();
}
Element date = e.getFirstChildByTagName("pubDate");
if(date != null) {
item.date = date.getChildAt(0).getText();
}
Element thumb = e.getFirstChildByTagName("media:thumbnail");
if(thumb != null) {
item.thumbUrl = thumb.getAttribute("url");
}
results.add(item);
}
 
return results;
}


In addition to the parsing code, there is also a helper class RSSItem for holding the parsed data. The network response byte array is wrapped to ByteArrayInputStrem and a InputStreamReader in order to use the XMLParser. The parser consumes a Reader object. The parser returns the root Element of the feed. The data is then parsed by traversing the document tree.

The parsed data is then returned as a list of RSSItem objects. CodenameOne List models use Hashtable objects in their model. The data in the hash table is rendered on the list items based on the keys. By default the List expects that the keys are like so: "Line1", Line2", "Line3", "Line4", "icon". The LineX elements should be Strings and the "icon" should be Image. Missing keys are simply ignored and the default implementation adapts based on the data available in the Hashtable. The text items can be simply set by value, but for the Images an extra step is needed:

if(item.thumbUrl != null) {
//Use the ImageDownloadService to load the image in background and update the list model when done.
Dimension imgDimen = new Dimension(item.thumbWidth, item.thumbHeight);
ImageDownloadService.createImageToStorage(item.thumbUrl, list, i, "icon", item.thumbUrl, null);
}


The ImageDownloadService is used to download the images on background and update the list when the image is ready for use. The ImageDownloadService.createImageToStorage(...) parameters are image url, the list component, index of the item for which the image is, key for the value when it is stored to the Hashtable, unique cache key and optionally image size for the decoded image. The ImageDownloadService simplifies our image downloading, decoding and memory management greatly by doing everything behind the scenes. The ImageDownloadService also has helper methods for displaying images in Label components.

File:RSSExample NetBeans 8.zip

LayoutAnimation

This example demonstrates how easy it is to animate layout changes with CodeNameOne. Start by creating a new project using the "Hello World (Manual)" template. Now add the following code to MyApplication.java

 
public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form();
hi.setLayout(new GridLayout(3,3));
for(int i = 1; i < 10; i++) {
Button b = new Button(Integer.toString(i));
b.addActionListener(swapListener);
hi.addComponent(b);
}
hi.show();
}

The code creates a new Form and sets it to use a GridLayout. CodenameOne has multitude of different layouts available and the animation technique of this example can be used with any one of them. Now the form has a 3x3 grid, for which we generate 9 buttons and add an ActionListener to handle click events. Here is the ActionListener code:

    ActionListener swapListener = new ActionListener() {
 
public void actionPerformed(ActionEvent evt) {
Component c = evt.getComponent();
Container parent = c.getParent();
parent.removeComponent(c);
parent.addComponent(0, c);
parent.animateLayout(300);
c.requestFocus();
}
};


The listener does not have a reference to the Form so the parent of the clicked Component is used instead. The component is first removed from it's parent and then re-added to be the first item of the grid. Now all that is needed for this change to be animated is for us to call parent.animateLayout(animationTimeInMs) instead of usual Component.revalidate()

File:LayoutAnimationExample Netbeans 8 project.zip

SimpleDrawing

This example creates a new Component which draws a filled polygon. The polygon is randomly generated each time user presses a key or touches the screen. Start by creating a new project with the "Hello World (Manual)" template. Add the following code to the MyApplication.java

 public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form("Simple Draw");
hi.setLayout(new BorderLayout());
PolygonComponent pc = new PolygonComponent();
//BorderLayout.CENTER used to make the component use the whole Form area
hi.addComponent(BorderLayout.CENTER, pc);
hi.show();
}


The code simply creates a new Form and adds our PolygonComponent to it. Using BorderLayout and only having the CENTER component present makes our component to fill the whole Form. Here is the code for the PolygonComponent

class PolygonComponent extends Component {
int[] xPoints;
int[] yPoints;
int pointCount = 10;
Random rand = new Random();
 
private void generatePolygon() {
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
//Less than 3 points would draw nothing
pointCount = 3 + rand.nextInt(64);
xPoints = new int[pointCount];
yPoints = new int[pointCount];
 
int maxRadius = Math.min(getWidth(), getHeight()) / 2;
for(int i = 0; i < pointCount; i++) {
double r = rand.nextFloat() * maxRadius;
double angle = Math.PI * 2 * i / pointCount;
int x = centerX + (int)(r * Math.cos(angle));
int y = centerY + (int)(r * Math.sin(angle));
xPoints[i] = x;
yPoints[i] = y;
}
}
 
@Override
public void paintBackground(Graphics g) {
g.setColor(0x0000ff); //0xRRGGBB
g.fillRect(0, 0, getWidth(), getHeight());
}
 
@Override
public void paint(Graphics g) {
//First draw -> generate polygon to draw
if(xPoints == null) {
generatePolygon();
}
g.setColor(0x00ff00); //0xRRGGBB
g.setAntiAliased(true);
g.fillPolygon(xPoints, yPoints, pointCount);
}
 
@Override
public void pointerPressed(int x, int y) {
generatePolygon();
}
 
@Override
public void keyPressed(int keyCode) {
generatePolygon();
}
}


The paintBackground(Graphics g) and paint(Graphics g) are overridden to do our custom painting. The former is used to paint the background and is really not that interesting. The latter is where most of the action happens. First we check if a polygon is generated and generate one if needed. The Graphics.fillPolygon() takes x- and y-coordinates in separate arrays, and our generatePolygon generates these arrays for us. Our polygon might have sharp edges so anti-aliasing is switched on by calling Graphics.setAntiAliased(true). The drawing handles color information as an integer of 0xRRGGBB. Alpha would need to be set by calling setAlpha(0..0xff).

The overridden pointerPressed(int x, int y) method intercepts touch events. The code simply generates a new polygon shape and just ignores the touch location parameters. The keyPressed(int keycode) is also overridden to generate new polygon if the user presses a key.

File:SimpleDrawExample NetBeans 8.zip

AnimatedPolygon

The last example uses UITimer to animate a polygon drawing Component. Start by creating a new project with the "Hello World (Manual)" template. Add the following code to the MyApplication.java

private final int CMD_PLAY = 1, CMD_PAUSE = 2;
public void start() {
if(current != null){
current.show();
return;
}
 
final Form f = new Form("Animated Polygon");
final PolygonComponent pc = new PolygonComponent();
 
f.setLayout(new BorderLayout());
//Using borderLayout.CENTER to make the PolygonComponent use the whole form area
f.addComponent(BorderLayout.CENTER, pc);
 
//UITimer sends repaint calls to the PolygonComponent in the EDT
final UITimer timer = new UITimer(new Runnable() {
 
public void run() {
pc.repaint();
}
});
 
//Add commands to start and stop the animation timer
f.addCommand(new Command("Play", CMD_PLAY));
f.addCommand(new Command("Pause", CMD_PAUSE));
f.addCommandListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
int cmd = evt.getCommand().getId();
switch(cmd) {
case CMD_PAUSE:
timer.cancel();
break;
case CMD_PLAY:
timer.schedule(10, true, f);
break;
}
}
});
 
f.show();
}

The code creates a new Form with a PolygonComponent. In addition a UITimer is initialized. The timer is needed to update the PolygonComponetn so a Runneable that calls the components repaint() method is passed to the constructor. Two Commands "Play" and "Pause" are added to the form. Commands are given ids to make the command handling code easier with switch - case. An ActionListener is added to the form to handle start and stop the animation when user uses the commands. The play command starts the timer with the code timer.schedule(10, true, f). This means that the timer fires every 10ms, repeats and is bound to our Form. The UITimer fires events in the Event Dispatch Thread, which is essential when interacting with the UI. All that is left is the PolygonComponent.

class PolygonComponent extends Component {
int bgColor;
int fgColor;
final int POINT_COUNT = 32;
int[] xPoints = new int[POINT_COUNT];
int[] yPoints = new int[POINT_COUNT];
double[] radiuses = new double[POINT_COUNT];
double[] angles = new double[POINT_COUNT];;
double[] animationSpeeds = new double[POINT_COUNT];;
Random rand = new Random();
 
public PolygonComponent() {
generatePolygon();
}
 
private void generatePolygon() {
//Generate random colors for polygon and the background
//0xRRGGBB
bgColor = rand.nextInt() & 0xffffff;
fgColor = rand.nextInt() & 0xffffff;
for(int i = 0; i < POINT_COUNT; i++) {
double r = rand.nextDouble();
double angle = Math.PI * 2 * i / POINT_COUNT;
radiuses[i] = r;
angles[i] = angle;
animationSpeeds[i] = rand.nextDouble() / 100 + 0.01;
}
}
 
private void animatePolygon() {
int rMax = Math.min(getWidth(), getHeight()) / 2;
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
for(int i = 0; i < POINT_COUNT; i++) {
//increase the radius by the animationSpeed
radiuses[i] += animationSpeeds[i];
//change animationSpeed direction if needed
if(radiuses[i] < 0 || radiuses[i] > 1){
animationSpeeds[i] = -animationSpeeds[i];
}
//calculate the on-screen radius
double r = radiuses[i] * rMax;
xPoints[i] = centerX + (int)(r * Math.cos(angles[i]));
yPoints[i] = centerY + (int)(r * Math.sin(angles[i]));
}
}
 
@Override
public void paintBackground(Graphics g) {
g.setColor(bgColor);
g.fillRect(0, 0, getWidth(), getHeight());
}
 
@Override
public void paint(Graphics g) {
animatePolygon();
g.setColor(fgColor);
g.setAntiAliased(true);
g.fillPolygon(xPoints, yPoints, POINT_COUNT);
}
 
@Override
public void pointerPressed(int x, int y) {
generatePolygon();
}
 
@Override
public void keyPressed(int keyCode) {
generatePolygon();
}
}


This version of the PolygonContainer is for the most parts similar to the one in the previous example. The main difference is that the polygon is slightly modified each time the view is drawn by calling animatePolygon() before the g.fillPolygon(...). The polygon generator method is also modified a bit from the previous example and new colors are randomly generated for both background and the polygon.

File:AnimatedPolygon.zip

This page was last modified on 11 July 2014, at 11:41.
8350 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×