Fork us on GitHub

TIP: Table to Box

How to find stuff on our website
TIP: Table to Box

TIP: Table to Box

One of the most painful aspects in any mobile app is input, besides the difficulty of viewing the details on a tiny cramped screen the input via the virtual keyboard is nowhere near the input comfort of a full fledged computer or even a tablet.

A great app adapts to the screen size and uses the available space more effectively, we tried to do this in the kitchen sink demo where user input appears like this in portrait:

The kitchen sink input demo in portrait mode
Figure 1. The kitchen sink input demo in portrait mode
The kitchen sink input demo in landscape mode
Figure 2. The kitchen sink input demo in landscape mode

You will notice that the demo uses table layout when in landscape (or running on a tablet) and a box layout on the Y_AXIS when running in a phone in portrait to use the space better. One of the things that isn’t obvious from the screenshots above is the nice animation we get when rotating the device.

The code that makes this possible is really simple and should be easily adaptable to any input form you might have:

private void addComps(Form parent, Container cnt, Component... cmps) { (1)
    if(Display.getInstance().isTablet() || !Display.getInstance().isPortrait()) { (2)
        TableLayout tl = new TableLayout(cmps.length / 2, 2);
        cnt.setLayout(tl);
        tl.setGrowHorizontally(true);
        for(Component c : cmps) {
            if(c instanceof Container) { (3)
                cnt.add(tl.createConstraint().horizontalSpan(2), c);
            } else {
                cnt.add(c);
            }
        }
    } else {
        cnt.setLayout(BoxLayout.y());
        for(Component c : cmps) {
            cnt.add(c);
        }
    }
    if(cnt.getClientProperty("bound") == null) { (4)
        cnt.putClientProperty("bound", "true");
        if(!Display.getInstance().isTablet()) {
            parent.addOrientationListener((e) -> {
                Display.getInstance().callSerially(() -> {
                    cnt.removeAll(); (5)
                    addComps(parent, cnt, cmps);
                    cnt.animateLayout(800);
                });
            });
        }
    }
}

addComps(parent, comps,
        new Label("Name", "InputContainerLabel"),
        name,
        new Label("E-Mail", "InputContainerLabel"),
        email,
        new Label("Password", "InputContainerLabel"),
        password,
        BorderLayout.center(new Label("Birthday", "InputContainerLabel")).
                add(BorderLayout.EAST, birthday),
        new Label("Bio", "InputContainerLabel"),
        bio,
        BorderLayout.center(new Label("Join Mailing List", "InputContainerLabel")).
                add(BorderLayout.EAST, joinMailingList));
1 The addComps method just adds all the components to a dynamically changing input. You can use this method almost "as is" to get the effect above
2 In tablets/desktops we always use the table mode so the box layout only applies in portrait phones and nowhere else
3 We have a special case for container rows where we might have a custom UI element, in this case we always make it span the whole row even in a table layout mode
4 Without these lines the listener code would be bound twice and the layouts would blink
5 The animation logic just recurses the method so the elements are re-added with the right constraint then animated. Since the elements already have the right position the layout animation will produce that great rotation effect

Final Word

What makes an app delightful is the attention to detail, small changes like this make the UX of an app and it’s esthetic far more appealing.

Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.