Adapting to Tablets and Desktops - Part 3 - Transcript.pdf
1. Adapting to Tablets & Desktops - Part III
In this module we dig into the TabletUI class
2. public void start() {
if(current != null){
current.show();
return;
}
AppSettings app = AppStorage.getInstance().fetchAppSettings();
if(app.logo.get() == null) {
app.setLogo(theme.getImage("icon.png"));
app.setTitleBackground(theme.getImage("title-image.jpg"));
}
if(Display.getInstance().isTablet()) {
Toolbar.setPermanentSideMenu(true);
new TabletUI(app);
}
BaseNavigationForm.showDishForm(app);
Display.getInstance().callSerially(() -> {
Display.getInstance().registerPush(new Hashtable(), true);
});
}
RestaurantAppBuilder
The tablet UI is created by the main class of the application, we initialize it here so when base navigation form uses the generic code it would already be initialized. We
also initialize the permanent side menu which makes the side menu stay open all the time without the button. That’s a UI paradigm that makes sense on tablets and
desktops.
3. public class TabletUI extends Form {
private static TabletUI instance;
public TabletUI(AppSettings app) {
super(new BorderLayout());
instance = this;
Toolbar tb = getToolbar();
Style titleStyle = tb.getUnselectedStyle();
titleStyle.setBorder(Border.createEmpty());
titleStyle.setBgImage(app.getTitleBackground());
titleStyle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
titleStyle.setPaddingUnit(Style.UNIT_TYPE_DIPS);
titleStyle.setPadding(4, 4, 4, 4);
TextField title = new TextField(Restaurant.getInstance().name.get());
title.setUIID("NavigationTitle");
title.addActionListener(a -> {
Restaurant.getInstance().name.set(title.getText());
new Builder().updateRestaurantSettings(app);
});
TextField tagline = new TextField(Restaurant.getInstance().tagline.get());
tagline.setUIID("Tagline");
TabletUI
The tablet UI class is a form that encapsulates the full layout on a tablet
It’s a singleton because we have just one form in the application and one UI
4. public class TabletUI extends Form {
private static TabletUI instance;
public TabletUI(AppSettings app) {
super(new BorderLayout());
instance = this;
Toolbar tb = getToolbar();
Style titleStyle = tb.getUnselectedStyle();
titleStyle.setBorder(Border.createEmpty());
titleStyle.setBgImage(app.getTitleBackground());
titleStyle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
titleStyle.setPaddingUnit(Style.UNIT_TYPE_DIPS);
titleStyle.setPadding(4, 4, 4, 4);
TextField title = new TextField(Restaurant.getInstance().name.get());
title.setUIID("NavigationTitle");
title.addActionListener(a -> {
Restaurant.getInstance().name.set(title.getText());
new Builder().updateRestaurantSettings(app);
});
TextField tagline = new TextField(Restaurant.getInstance().tagline.get());
tagline.setUIID("Tagline");
TabletUI
We use a border layout and place the content of the UI in the center so we can replace it when navigating between UIAbstraction instances
5. public class TabletUI extends Form {
private static TabletUI instance;
public TabletUI(AppSettings app) {
super(new BorderLayout());
instance = this;
Toolbar tb = getToolbar();
Style titleStyle = tb.getUnselectedStyle();
titleStyle.setBorder(Border.createEmpty());
titleStyle.setBgImage(app.getTitleBackground());
titleStyle.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
titleStyle.setPaddingUnit(Style.UNIT_TYPE_DIPS);
titleStyle.setPadding(4, 4, 4, 4);
TextField title = new TextField(Restaurant.getInstance().name.get());
title.setUIID("NavigationTitle");
title.addActionListener(a -> {
Restaurant.getInstance().name.set(title.getText());
new Builder().updateRestaurantSettings(app);
});
TextField tagline = new TextField(Restaurant.getInstance().tagline.get());
tagline.setUIID("Tagline");
TabletUI
The title and subtitle are editable here too, this is effectively a copy of the code from base navigation form adapted for the tablet form factor
6. tagline.addActionListener(a -> {
Restaurant.getInstance().tagline.set(tagline.getText());
new Builder().updateRestaurantSettings(app);
});
tb.setTitleComponent(BoxLayout.encloseY(title, tagline));
Button logoImage = new Button("", app.getRoundedScaledLogo(), "TabletLogoImage");
logoImage.addActionListener(e -> {
new ImagePickerForm(512, 512, "Icon", app.getLogo(),
newImage -> {
app.setLogo(newImage);
logoImage.setIcon(app.getRoundedScaledLogo());
}).show();
});
tb.addComponentToSideMenu(logoImage);
ButtonGroup bg = new ButtonGroup();
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Dish List",
FontImage.MATERIAL_RESTAURANT_MENU, true,
e -> BaseNavigationForm.showDishForm(app)));
TabletUI
This is also a copy from base navigation form, but thanks to the available space we don’t need to jump thru hoops to make this look good or fit. So the code here is
much simpler
7. tagline.addActionListener(a -> {
Restaurant.getInstance().tagline.set(tagline.getText());
new Builder().updateRestaurantSettings(app);
});
tb.setTitleComponent(BoxLayout.encloseY(title, tagline));
Button logoImage = new Button("", app.getRoundedScaledLogo(), "TabletLogoImage");
logoImage.addActionListener(e -> {
new ImagePickerForm(512, 512, "Icon", app.getLogo(),
newImage -> {
app.setLogo(newImage);
logoImage.setIcon(app.getRoundedScaledLogo());
}).show();
});
tb.addComponentToSideMenu(logoImage);
ButtonGroup bg = new ButtonGroup();
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Dish List",
FontImage.MATERIAL_RESTAURANT_MENU, true,
e -> BaseNavigationForm.showDishForm(app)));
TabletUI
The logo image is now just an icon on the side menu which makes it look far better overall
8. tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Details",
FontImage.MATERIAL_DESCRIPTION,
e -> BaseNavigationForm.showDetailsForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Billing",
FontImage.MATERIAL_CREDIT_CARD,
e -> BaseNavigationForm.showBillingForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Build the App",
FontImage.MATERIAL_PHONE_IPHONE,
e -> BaseNavigationForm.showAppForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "About Us",
FontImage.MATERIAL_INFO,
e -> BaseNavigationForm.showAboutForm(app)));
tb.addCommandToRightBar("Edit Background", null, e -> {
new ImagePickerForm(750, 295, "Background Image", app.getTitleBackground(),
newImage -> {
app.setTitleBackground(newImage);
titleStyle.setBgImage(newImage);
}).show();
});
}
TabletUI
You’ll notice that all the side menu entries are marked here but I’ll only highlight one as they are all mostly the same. Since we don’t derive from the base navigation form
this code is a bit duplicate but it’s much simpler as we don’t need the whole hack with marking the selection.
Notice we aren’t adding commands but rather adding components using the generic createSideNavigationEntry method. We’ll discuss that method soon but notice the
bg argument we pass to each one of them. That’s a button group and these methods return a radio button this allows us to have the selection effect
9. tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Details",
FontImage.MATERIAL_DESCRIPTION,
e -> BaseNavigationForm.showDetailsForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Billing",
FontImage.MATERIAL_CREDIT_CARD,
e -> BaseNavigationForm.showBillingForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "Build the App",
FontImage.MATERIAL_PHONE_IPHONE,
e -> BaseNavigationForm.showAppForm(app)));
tb.addComponentToSideMenu(createSideNavigationEntry(bg, "About Us",
FontImage.MATERIAL_INFO,
e -> BaseNavigationForm.showAboutForm(app)));
tb.addCommandToRightBar("Edit Background", null, e -> {
new ImagePickerForm(750, 295, "Background Image", app.getTitleBackground(),
newImage -> {
app.setTitleBackground(newImage);
titleStyle.setBgImage(newImage);
}).show();
});
}
TabletUI
We also add the edit background image button to the side, it’s relatively trivial as there isn’t that much we need to do here.
10. private RadioButton createSideNavigationEntry(ButtonGroup bg, String title, char icon,
ActionListener<ActionEvent> ac) {
return createSideNavigationEntry(bg, title, icon, false, ac);
}
private RadioButton createSideNavigationEntry(ButtonGroup bg, String title, char icon,
boolean selected, ActionListener<ActionEvent> ac) {
RadioButton a = RadioButton.createToggle(title, bg);
a.setSelected(selected);
a.setUIID("SideCommand");
FontImage.setMaterialIcon(a, icon, 4);
a.addActionListener(ac);
return a;
}
void showContainer(Container cnt, boolean direction) {
if(getContentPane().getComponentCount() == 0) {
add(BorderLayout.CENTER, cnt);
show();
} else {
getContentPane().replace(getContentPane().getComponentAt(0), cnt,
CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL,
direction, 150));
}
}
TabletUI
createSideNavigationEntry just generalizes the code that creates a radio button toggle button. It mostly saves some boilerplate and doesn’t feature much logic
11. private RadioButton createSideNavigationEntry(ButtonGroup bg, String title, char icon,
ActionListener<ActionEvent> ac) {
return createSideNavigationEntry(bg, title, icon, false, ac);
}
private RadioButton createSideNavigationEntry(ButtonGroup bg, String title, char icon,
boolean selected, ActionListener<ActionEvent> ac) {
RadioButton a = RadioButton.createToggle(title, bg);
a.setSelected(selected);
a.setUIID("SideCommand");
FontImage.setMaterialIcon(a, icon, 4);
a.addActionListener(ac);
return a;
}
void showContainer(Container cnt, boolean direction) {
if(getContentPane().getComponentCount() == 0) {
add(BorderLayout.CENTER, cnt);
show();
} else {
getContentPane().replace(getContentPane().getComponentAt(0), cnt,
CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL,
direction, 150));
}
}
TabletUI
However, showContainer is pretty interesting. It’s the method that’s effectively invoked when show() or showBack() are invoked.
There are two modes here. For the very first screen we need to use add. From that point on we’ll always use replace to show the next UIAbstraction instance.