Port Native Android App to iOS: Part III

DZone 's Guide to

Port Native Android App to iOS: Part III

With this final article, we will finish up converting a simple Android application to a Codename One application.

· Mobile Zone ·
Free Resource

In Part I of this series, we discussed the port of Swiftnotes to iOS by porting it to Codename One and covered what Codename One is and how it differs from Android. In Part II, we built the skeleton of the app which we will finally finish here.

As a reminder, the finished app is on Apple iTunes, Google Play, and the Microsoft Store as well as online with a JavaScript build which you can see here.

The full source code of the app is here: https://github.com/codenameone/SwiftnotesCN1

Step 6: Styling the UI

We style the UI using the designer tool, there are other options such as CSS but I find the designer tool to be simpler and more consistent.

To launch the designer double click the theme.res file in the src root. Select the Theme entry and press the Add button in the bottom of the theme. In the Combo Box at the top of the Add dialog type "Toolbar".

Select the Color tab.

Next, uncheck Derive Background and paste the color (which I got from the colors.xml file of the original Android project) CE0A31. Then uncheck Derive Transparency and set the value to 255.

Next select the Border tab, uncheck Derive click the button and select Empty in the combo box.

Next select the Padding tab, uncheck Derive and leave everything as 0.

You should end up with something like this and a red background title:

Setup of the Toolbar UIID Figure 7. Setup of the Toolbar UIID

Next we setup the foreground colors and fonts for the title elements, for brevity I will summerize the elements that need changing.

Add a "Title" entry with the following properties:

  • Foreground Color ffffff
  • Background Transparency 0
  • Font True Type = native:MainLight (this will map to Roboto/Helvetic Neue respectively)True Type Size = 4 millimeters

After saving this copy and paste this entry as TitleCommand & MenuButton.

Last but not least we will add a StatusBar UIID with:

  • Background color of B00A31
  • Background Transparency 255

We now have this which is already starting to look like an app:

After styling the title

Figure 8. After styling the title

Step 7: Convert the Functionality

The other UI elements are too simple and often inconsistent so we’ll just delete the corresponding .gui & .java files representing those other widgets and implement them dynamically. This is especially crucial since the original app has some "androidisms" embedded into it making the code conversion challenging.

E.g. the original app didn’t include a Note business object abstraction which is pretty basic so we added that to get started:

public class Note implements Externalizable {
    private static ArrayList<Note> notes;

    private String title = "";
    private String body = "";
    private boolean bodyHidden;
    private boolean starred;
    private int color = 0xffffff;
    private float fontSize = 2;
    private boolean deleted;

     * @return the title
    public String getTitle() {
        return title;

     * @param title the title to set
    public void setTitle(String title) {
        this.title = title;

     * @return the body
    public String getBody() {
        return body;

     * @param body the body to set
    public void setBody(String body) {
        this.body = body;

     * @return the bodyHidden
    public boolean isBodyHidden() {
        return bodyHidden;

     * @param bodyHidden the bodyHidden to set
    public void setBodyHidden(boolean bodyHidden) {
        this.bodyHidden = bodyHidden;

     * @return the starred
    public boolean isStarred() {
        return starred;

     * @param starred the starred to set
    public void setStarred(boolean starred) {
        this.starred = starred;

     * @return the color
    public int getColor() {
        return color;

     * @param color the color to set
    public void setColor(int color) {
        this.color = color;

     * @return the fontSize
    public float getFontSize() {
        return fontSize;

     * @param fontSize the fontSize to set
    public void setFontSize(float fontSize) {
        this.fontSize = fontSize;

    public int getVersion() {
        return 1;

    public void externalize(DataOutputStream out) throws IOException {
        Util.writeUTF(title, out);
        Util.writeUTF(body, out);

    public void internalize(int version, DataInputStream in) throws IOException {
        title = Util.readUTF(in);
        body = Util.readUTF(in);
        bodyHidden = in.readBoolean();
        starred = in.readBoolean();
        color = in.readInt();
        fontSize = in.readFloat();

    public String getObjectId() {
        return "Note";

    public static ArrayList<Note> getNotes() {
        if(notes == null) {
            notes = (ArrayList<Note>)Storage.getInstance().readObject("notes");
            if(notes == null) {
                notes = new ArrayList<>();
        return notes;

    public void saveNote() {
        if(!notes.contains(this)) {
        Storage.getInstance().writeObject("notes", notes);

    public void delete() {
        deleted = true;
        Storage.getInstance().writeObject("notes", notes);

    public boolean isDeleted() {
        return deleted;

Notice that this class encapsulates all the handling/storage of notes, I kept it as simple as possible to avoid any complexities and just implemented simple serialization for persistence.

Once we have that abstraction everything else becomes trivial, all we need is a single additional class we have below:

public class EditNote extends Form {
    private static final int[] COLORS = {
        0x44a1eb, 0x77ddbb, 0xbbe535,
        0xeeee22, 0xffbb22, 0xf56545,
        0xff5997, 0xa767ff, 0xffffff

    Command hideShowCommand;

    public EditNote(Note n, boolean isNew, ActivityMain parentForm) { 
        super("", new BorderLayout()); 
        TextField title = new TextField(n.getTitle(), "Title", 20, TextArea.ANY);
        TextArea body = new TextArea(n.getBody());
        add(BorderLayout.NORTH, title);
        add(BorderLayout.CENTER, body);
        Font fnt = body.getUnselectedStyle().getFont(); 
        body.getAllStyles().setFont(fnt.derive(Display.getInstance().convertToPixels(n.getFontSize()), Font.STYLE_PLAIN));

        getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_PALETTE, e -> {
            Dialog colorPicker = new Dialog("dialog_note_colour"); 
            colorPicker.setBackCommand("", null, ee -> colorPicker.dispose());
            colorPicker.setLayout(new GridLayout(3, 3));
            for(int iter = 0 ; iter < COLORS.length ; iter++) {
                Button choose = new Button("");
                Style s = choose.getAllStyles();
                int color = COLORS[iter];
                if(color == getContentPane().getUnselectedStyle().getBgColor()) {
                    FontImage.setMaterialIcon(choose, FontImage.MATERIAL_CHECK_CIRCLE, 3.5f);
                choose.addActionListener(ee -> {
            colorPicker.showPacked(BorderLayout.CENTER, true);

        getToolbar().setBackCommand("", e -> {
            if(isNew) {
                if(Dialog.show("Save Changes", "", "Yes", "No")) {
            } else {

        getToolbar().addMaterialCommandToOverflowMenu("Font Size", FontImage.MATERIAL_FORMAT_SIZE, e -> {
            Slider s = new Slider(); 
            s.setProgress(Math.round(n.getFontSize() * 10));
            InteractionDialog id = new InteractionDialog();
            id.setLayout(new BorderLayout());
            id.add(BorderLayout.CENTER, s);
            s.addDataChangedListener((i, ii) -> {
                n.setFontSize(1 + ((float)s.getProgress()) / 10.0f);
                body.getAllStyles().setFont(fnt.derive(Display.getInstance().convertToPixels(n.getFontSize()), Font.STYLE_PLAIN));
            Button ok = new Button("OK");
            id.add(BorderLayout.SOUTH, ok);
            ok.addActionListener(ee -> id.dispose());
            id.show(getLayeredPane().getHeight() - Display.getInstance().convertToPixels(10), 0, 0, 0);


    void addHideShowCommand(Note n) { 
        if(hideShowCommand != null) {
        if(n.isBodyHidden()) {
            hideShowCommand = getToolbar().addMaterialCommandToOverflowMenu("Show Body", FontImage.MATERIAL_VISIBILITY, e -> {
        } else {
            hideShowCommand = getToolbar().addMaterialCommandToOverflowMenu("Hide Body", FontImage.MATERIAL_VISIBILITY_OFF, e -> {

I mixed several concepts from the original code (e.g. the color picker etc) into a single entry.

1> The constructor of the form accepts the parent form and whether this is a new note. We need this to determine whether to add a new note when going back.
2> We use border layout which allows us to place the title in the north and the body in the center. This is very convenient to take up available space
3> We set some UI elements directly instead of via UIID setting in the designer, this allows us to modify things in runtime and is sometimes more convenient than going thru the design tool. In this case we need to set the font size that might differ per note
4> The color picker dialog is just a regular dialog with a grid layout that shows a round border to give the color options. Since the code was so simple I didn’t see the need to move it to another class.
5> The back command does the save operation for consistency with the original app. Personally I would rather have a check option to save rather than show a dialog but I preferred consistency over personal preference
6> Instead of 3 hardcoded sizes I chose to show a slider in an interaction dialog that allows editing the UI while working with the dialog. This creates a nice effect where you can drag the slider and instantly see the font grow/shrink
7> The command in the overflow menu changes based on state so I placed this in a separate method so it can be removed/re-added

Now comes the point of binding this together into the first form… We’ll change the constructor to this:

public ActivityMain(com.codename1.ui.util.Resources resourceObjectInstance) {
    FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD);
    fab.addActionListener(e -> { 
        Note n = new Note();
        new EditNote(n, true, ActivityMain.this).show();

    ArrayList<Note> notes = Note.getNotes(); 
    if(notes.size() > 0) {
        for(Note n : notes) {

    getToolbar().addSearchCommand(e -> search((String)e.getSource())); 
    getToolbar().addCommandToOverflowMenu("Backup Notes", null, e -> Log.p("Todo"));
    getToolbar().addCommandToOverflowMenu("Restore Notes", null, e -> Log.p("Todo"));
    getToolbar().addCommandToOverflowMenu("Rate App", null, e -> Log.p("Todo"));

There are several interesting things here:

1> We listen to an event on the floating action button then launch the new edit UI code
2> We load the notes and place them into the UI, I’ll get into addNote below
3> Search is one line of code which we list below

Let's start with add/create note which is one of the trickier methods here:

private Container createNoteCnt(Note n) {
    Button title = new Button(n.getTitle()); 
    CheckBox star = CheckBox.createToggle(""); 
    FontImage.setMaterialIcon(star, FontImage.MATERIAL_STAR_BORDER, 4);
    star.setPressedIcon(FontImage.createMaterial(FontImage.MATERIAL_STAR, "NoteTitle", 4));
    star.addActionListener(e -> {
    Container cnt;
    if(!n.isBodyHidden()) { 
        TextArea body = new TextArea(n.getBody());
        Font fnt = body.getUnselectedStyle().getFont();
        body.getAllStyles().setFont(fnt.derive(Display.getInstance().convertToPixels(n.getFontSize()), Font.STYLE_PLAIN));
        cnt = BorderLayout.center(
                    BoxLayout.encloseY(title, body)
                ).add(BorderLayout.EAST, star);
    } else {
        cnt = BorderLayout.center(title).
                        add(BorderLayout.EAST, star);
    Button delete = new Button("");
    FontImage.setMaterialIcon(delete, FontImage.MATERIAL_DELETE, 4);
    SwipeableContainer sc = new SwipeableContainer(delete, cnt); 
    delete.addActionListener(e -> {
    title.addActionListener(e -> { 
        if(!n.isDeleted()) {
            new EditNote(n, false, this).show();
            addShowListener(ee -> {
                getContentPane().replace(sc, createNoteCnt(n), null);
    sc.putClientProperty("note", n); 
    return sc;

1> A note entry in the main screen is a lead component. A lead component is a unique concept to Codename One. In it a component (in this case the title button) takes the "lead" over the Container hierarchy so all clicks within that specific hierarchy, state changes etc. map to the button and its events. This means that clicking the text area below the button will act as if we clicked the button.
2> The star is a toggle button with two states, which solves a lot of the complexities. We exclude it from the lead component hierarchy so it can be toggled independently.
3> The body of the note can be hidden in which case we will have two states the font size of the body can be enlarged which is another special case we check here
4> The background is set in the Container to allow the whole thing to take up the selected color. I could have used a "round rect" background to look similar to the original but that design is somewhat outdated and I chose to go with the swipe container which works better with full row (it would look awkward with round borders).
5> We wrap everything in a SwipeableContainer to allow the user to swipe the UI and see the delete button. The original UI had a long press action for delete which is probably a bit outdated design wise.
6> If the user edits an existing node instead of writing code to apply the changes we just create a new node component and throw away the old node component.
7> Client properties allow us to store meta-data within a component. This is very useful and we use that data in the search method below!

The search method is really simple, it has a special case up front to cancel search for an empty string.

It then proceeds to loop over the components and hide/show based on the Note associated with the component.

Notice that we could have queried the component values but because the body might be hidden the hierarchy might be slightly different so this is a simpler solution overall.

We then animate the layout to move the components into place…

Minor Tunings for the Final Result

Since the overflow menu is overly "Android oriented" I decided to move everything into the title bar area which makes the app look better everywhere as there is space available there.

I also added a colors.xml file to allow native Android styling to use the theme colors, I just placed this file under the native/android directory:

It makes very subtle differences you would mostly see while task switching.

I also made some theme refinements adding padding in the right places and fixing some fonts/colors but nothing significant.

Final Thoughts

Porting the whole app and writing this took me 1 day during which I also did other typical tasks. I’m assuming it would have taken longer to a person who isn’t as familiar with Codename One as I am although that person wouldn’t need to write an article about it either…

Looking over the Android code and the corresponding Codename One code the differences are staggering. I’m (obviously) biased but the amount of code and it’s complexity is significantly lower in Codename One.

The porting from Android isn’t a trivial task as it does require you to know Codename One. The process has a lot of kinks some of which just aren’t fixable since we don’t want Codename One to venture into the complexity levels of Android.

However, if you express interest and start filing issues/participating with the conversion wizard work that Steve has done we will invest time and effort to match your time & effort!

This should make future iterations of this tutorial even simpler and produce far more satisfying initial results.

I’ve compared the native app side by side with the Codename One app and with the exception of the overflow menu which could use some work, our app looks just as "native" on an Android phone.

With one simpler, shorter code base I was able to reach iOS, Android, Windows & JavaScript not to mention desktop targets…

In this specific app, I can’t see any compromises in quality/appearance or functionality. This is obviously not the case for every app in existence but it is the case for many (most?) apps.

mobile ,android ,iphone ,ios ,porting

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}