SlideShare a Scribd company logo
Creating an Uber Clone - Part IX
The “Where to?” UI is a pretty big piece of the puzzle and it also includes the navigation UI which is the bottom portion
Destination UI
© Codename One 2017 all rights reserved
Up until now we focused on this part of the toolbar area
Destination UI
© Codename One 2017 all rights reserved
Now we need to do this portion. The destination UI toggle is a huge part of the navigation UI. It's the bottom section of the form that contains the list of destinations. We
can build it on top of the code we wrote for the navigation UI and place it in the center of the layer so it will play nicely with the rest of the UI.
Destination UI
© Codename One 2017 all rights reserved
For now I’ll ignore this potion as it’s mostly a specialization of other things and can be done relatively easily if you understand the rest of the things I did
private void showToNavigationBar(Container parentLayer) {
MultiButton addHome = new MultiButton("Add Home");
addHome.setUIID("Container");
addHome.setUIIDLine1("WhereToButtonLine1");
addHome.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(addHome, FontImage.MATERIAL_HOME);
MultiButton addWork = new MultiButton("Add Work");
addWork.setUIID("Container");
addWork.setUIIDLine1("WhereToButtonLine1");
addWork.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(addWork, FontImage.MATERIAL_WORK);
MultiButton savedPlaces = new MultiButton("Saved Places");
savedPlaces.setUIID("Container");
savedPlaces.setUIIDLine1("WhereToButtonLineNoBorder");
savedPlaces.setEmblemUIID("WhereToButtonLineNoBorder");
savedPlaces.setIconUIID("WhereToButtonIcon");
savedPlaces.setEmblem(FontImage.createMaterial(
FontImage.MATERIAL_NAVIGATE_NEXT,
savedPlaces.getIconComponent().getUnselectedStyle()));
FontImage.setMaterialIcon(savedPlaces,
FontImage.MATERIAL_STAR_BORDER);
Destination UI
Let’s jump right in to the “showNavigationBar” method.

The top elements are relatively simple multi-buttons, we use Container as their UIID so they will be transparent with 0 padding/margin.
savedPlaces.setEmblem(FontImage.createMaterial(
FontImage.MATERIAL_NAVIGATE_NEXT,
savedPlaces.getIconComponent().getUnselectedStyle()));
FontImage.setMaterialIcon(savedPlaces,
FontImage.MATERIAL_STAR_BORDER);
Label whereSeparator = new Label("", "WhereSeparator");
whereSeparator.setShowEvenIfBlank(true);
MultiButton history1 = new MultiButton("Mikve Yisrael Str...");
history1.setUIID("Container");
history1.setUIIDLine1("WhereToButtonLine1");
history1.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY);
Container result = BoxLayout.encloseY(addHome, addWork,
savedPlaces, whereSeparator, history1);
result.setUIID("Form");
result.setScrollableY(true);
result.setScrollVisible(false);
result.setY(getDisplayHeight());
Destination UI
The separator is just a label with a specific style. Notice that blank labels/buttons etc. are hidden by default in Codename One and you should invoke
setShowEvenIfBlank if you want such a label to still render
savedPlaces.setEmblem(FontImage.createMaterial(
FontImage.MATERIAL_NAVIGATE_NEXT,
savedPlaces.getIconComponent().getUnselectedStyle()));
FontImage.setMaterialIcon(savedPlaces,
FontImage.MATERIAL_STAR_BORDER);
Label whereSeparator = new Label("", "WhereSeparator");
whereSeparator.setShowEvenIfBlank(true);
MultiButton history1 = new MultiButton("Mikve Yisrael Str...");
history1.setUIID("Container");
history1.setUIIDLine1("WhereToButtonLine1");
history1.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY);
Container result = BoxLayout.encloseY(addHome, addWork,
savedPlaces, whereSeparator, history1);
result.setUIID("Form");
result.setScrollableY(true);
result.setScrollVisible(false);
result.setY(getDisplayHeight());
Destination UI
We can reuse the Form UIID here because that's effectively what we want. We want this UI to appear as if it's a Form
savedPlaces.setEmblem(FontImage.createMaterial(
FontImage.MATERIAL_NAVIGATE_NEXT,
savedPlaces.getIconComponent().getUnselectedStyle()));
FontImage.setMaterialIcon(savedPlaces,
FontImage.MATERIAL_STAR_BORDER);
Label whereSeparator = new Label("", "WhereSeparator");
whereSeparator.setShowEvenIfBlank(true);
MultiButton history1 = new MultiButton("Mikve Yisrael Str...");
history1.setUIID("Container");
history1.setUIIDLine1("WhereToButtonLine1");
history1.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY);
Container result = BoxLayout.encloseY(addHome, addWork,
savedPlaces, whereSeparator, history1);
result.setUIID("Form");
result.setScrollableY(true);
result.setScrollVisible(false);
result.setY(getDisplayHeight());
Destination UI
We need this to be scrollable but we don't want the scrollbar on the side as it might cause aesthetic issues
Label whereSeparator = new Label("", "WhereSeparator");
whereSeparator.setShowEvenIfBlank(true);
MultiButton history1 = new MultiButton("Mikve Yisrael Str...");
history1.setUIID("Container");
history1.setUIIDLine1("WhereToButtonLine1");
history1.setIconUIID("WhereToButtonIcon");
FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY);
Container result = BoxLayout.encloseY(addHome, addWork,
savedPlaces, whereSeparator, history1);
result.setUIID("Form");
result.setScrollableY(true);
result.setScrollVisible(false);
result.setY(getDisplayHeight());
result.setWidth(getDisplayWidth());
result.setHeight(result.getPreferredH());
parentLayer.add(CENTER, result);
parentLayer.animateLayout(200);
}
Destination UI
The showing of this element is animated from the bottom of the Form
WhereToButtonLine1
© Codename One 2017 all rights reserved
While this UI is very simple it did define a few UIID's. Let's start with WhereToButtonLine1 which represents the entries in the list of elements and also adds the underline.
The color is just black over transparent which in this case leads to white
WhereToButtonLine1
© Codename One 2017 all rights reserved
The padding on the left side is relatively low since the icon will take the extra padding. On the other sides we have typical 4 millimeter padding
WhereToButtonLine1
© Codename One 2017 all rights reserved
Margin is 0 as usual
WhereToButtonLine1
© Codename One 2017 all rights reserved
We put the underline here because the design placed the underline only under the text and didn't place it under the icon which has a different UIID. The underline is a
gray 2 pixel high line
WhereToButtonLine1
© Codename One 2017 all rights reserved
The font is the standard 3 millimeter light font
WhereToButtonIcon
© Codename One 2017 all rights reserved
The WhereToButtonIcon style applies to the icon which has less horizontal padding so it won’t drift too far from the text but identical vertical padding so it will align
properly with the text
WhereToButtonIcon
© Codename One 2017 all rights reserved
It derives from WhereToButtonLine1 so they will fit together well
WhereToButtonNoBorder
© Codename One 2017 all rights reserved
We need the no border version of the style so it will remove the underline border on the last entry. Otherwise we can see an out of place underline in that one last entry in
the list
WhereToButtonNoBorder
© Codename One 2017 all rights reserved
We derive from the same style so everything else is identical
WhereSeparator
© Codename One 2017 all rights reserved
The WhereSeparator is just a gray padded line, so it has the right gray color and is completely opaque with no background transparency.
WhereSeparator
© Codename One 2017 all rights reserved
It’s exactly 2 millimeters tall so it will stand out but won’t take out an entire line.
WhereSeparator
© Codename One 2017 all rights reserved
Margin is 0 so it can reach the edge of the parent container
from.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
if(layer.getComponentCount() > 1) {
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200, 150, () -> {
c.remove();
revalidate();
});
}
}
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
});
Focus Listener
Now that we added this we need to show this UI and hide it when the user toggles the focus in the text fields. We can do this by binding focus listeners to the to and
from text fields.

When focus is lost/gained we toggle between the square and circle modes by setting the icon to the appropriate labels
from.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
if(layer.getComponentCount() > 1) {
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200, 150, () -> {
c.remove();
revalidate();
});
}
}
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
});
Focus Listener
We always have one container in the layer except for the case where the second component is the where to container. It's always the second component because it's
always added last.
from.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
if(layer.getComponentCount() > 1) {
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200, 150, () -> {
c.remove();
revalidate();
});
}
}
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
});
Focus Listener
We set the position of this container below the forms
from.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
if(layer.getComponentCount() > 1) {
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200, 150, () -> {
c.remove();
revalidate();
});
}
}
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
});
Focus Listener
Animate "unlayout" moves the component outside of the screen to the position we asked for using a smooth animation
from.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(square);
if(layer.getComponentCount() > 1) {
Component c = layer.getComponentAt(1);
c.setY(getDisplayHeight());
layer.animateUnlayout(200, 150, () -> {
c.remove();
revalidate();
});
}
}
public void focusLost(Component cmp) {
fromSelected.setIcon(circle);
}
});
to.addFocusListener(new FocusListener() {
public void focusGained(Component cmp) {
fromSelected.setIcon(circle);
toSelected.setIcon(square);
showToNavigationBar(layer);
}
public void focusLost(Component cmp) {
toSelected.setIcon(circle);
}
});
Focus Listener
This callback is invoked when the unlayout completes. At this point we have an invalid UI that needs a layout but before we do that we remove the component that we
animated out of the form
back.addActionListener(e -> {
navigationToolbar.setY(-navigationToolbar.getHeight());
if(layer.getComponentCount() > 1) {
layer.getComponentAt(1).setY(getDisplayHeight());
}
navigationToolbar.getParent().animateUnlayout(200, 120, () -> {
layer.removeAll();
revalidate();
});
});
Back
Now that the UI appears we also need to remove it when going back so I'll update the back action listener from above to handle the "Where to?" UI as well.

This is the exact same unlayout operation we did before
navigationToolbar.getUnselectedStyle().setBgPainter((g1, rect) -> {
g1.setAlpha(255);
g1.setColor(0xffffff);
if(dropShadow != null) {
if(layer.getComponentCount() > 1) {
g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
g1.drawImage(dropShadow, rect.getX(), rect.getY() +
rect.getHeight() - dropShadow.getHeight(), rect.getWidth(),
dropShadow.getHeight());
g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() -
dropShadow.getHeight() / 2);
} else {
g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
}
g1.setColor(0xa4a4ac);
g1.setAntiAliased(true);
int x = fromSelected.getAbsoluteX() + fromSelected.getWidth() / 2 - 1;
int y = fromSelected.getAbsoluteY() + fromSelected.getHeight() / 2 +
circle.getHeight() / 2;
g1.fillRect(x, y, 2, toSelected.getAbsoluteY() - y +
toSelected.getHeight() / 2 - circle.getHeight() / 2);
});
Background Painter
And finally we need to make a subtle but important change to the background painter code from before. 

Because of the drop shadow a gap is formed between the top and bottom pieces so a special case here paints a white rectangle under the shadow to hide the gap.
Without that the shadow would appear on top of the map and not on top of a white background.

Once this is done opening the "Where To?" UI and toggling the fields should work as expected.

More Related Content

PDF
Creating an Uber Clone - Part II - Transcript.pdf
PDF
Creating an Uber Clone - Part VIII - Transcript.pdf
PDF
create-netflix-clone-06-client-ui_transcript.pdf
PDF
How do I - Create a List of Items - Transcript.pdf
PDF
Creating an Uber Clone - Part IX.pdf
PDF
Creating an Uber Clone - Part XXIV - Transcript.pdf
PDF
Extracting ui Design - part 6 - transcript.pdf
PDF
Extracting ui Design - part 4 - transcript.pdf
Creating an Uber Clone - Part II - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
How do I - Create a List of Items - Transcript.pdf
Creating an Uber Clone - Part IX.pdf
Creating an Uber Clone - Part XXIV - Transcript.pdf
Extracting ui Design - part 6 - transcript.pdf
Extracting ui Design - part 4 - transcript.pdf

Similar to Creating an Uber Clone - Part IX - Transcript.pdf (20)

PDF
Adapting a UI Design.pdf
PDF
Creating an Uber Clone - Part XX - Transcript.pdf
PDF
Adapting to Tablets and Desktops - Part 1 - Transcript.pdf
PDF
Creating an Uber Clone - Part V - Transcript.pdf
PDF
Animations - Part 1 - Transcript.pdf
PDF
Creating a Facebook Clone - Part IX.pdf
PDF
Creating a Facebook Clone - Part XV - Transcript.pdf
PDF
Creating an Uber Clone - Part XXII - Transcript.pdf
PDF
Creating an Uber Clone - Part VIII.pdf
PDF
Initial UI Mockup - Part 2 - Transcript.pdf
PDF
Creating an Uber Clone - Part XXXVI - Transcript.pdf
PDF
UI Design From Scratch - Part 5 - transcript.pdf
PDF
Creating an Uber Clone - Part III - Transcript.pdf
PDF
Styles - Part 1 - Transcript.pdf
PDF
The App Maker - Part 2 - Transcript.pdf
PDF
Creating an Uber Clone - Part XXIII - Transcript.pdf
PDF
Creating an Uber Clone - Part XXIV.pdf
PDF
Creating an Uber Clone - Part XIX - Transcript.pdf
PDF
How do I - Style Backgrounds and Borders - Transcript.pdf
PDF
Extracting ui Design - part 3 - transcript.pdf
Adapting a UI Design.pdf
Creating an Uber Clone - Part XX - Transcript.pdf
Adapting to Tablets and Desktops - Part 1 - Transcript.pdf
Creating an Uber Clone - Part V - Transcript.pdf
Animations - Part 1 - Transcript.pdf
Creating a Facebook Clone - Part IX.pdf
Creating a Facebook Clone - Part XV - Transcript.pdf
Creating an Uber Clone - Part XXII - Transcript.pdf
Creating an Uber Clone - Part VIII.pdf
Initial UI Mockup - Part 2 - Transcript.pdf
Creating an Uber Clone - Part XXXVI - Transcript.pdf
UI Design From Scratch - Part 5 - transcript.pdf
Creating an Uber Clone - Part III - Transcript.pdf
Styles - Part 1 - Transcript.pdf
The App Maker - Part 2 - Transcript.pdf
Creating an Uber Clone - Part XXIII - Transcript.pdf
Creating an Uber Clone - Part XXIV.pdf
Creating an Uber Clone - Part XIX - Transcript.pdf
How do I - Style Backgrounds and Borders - Transcript.pdf
Extracting ui Design - part 3 - transcript.pdf

More from ShaiAlmog1 (20)

PDF
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
PDF
create-netflix-clone-06-client-ui.pdf
PDF
create-netflix-clone-01-introduction_transcript.pdf
PDF
create-netflix-clone-02-server_transcript.pdf
PDF
create-netflix-clone-04-server-continued_transcript.pdf
PDF
create-netflix-clone-01-introduction.pdf
PDF
create-netflix-clone-03-server.pdf
PDF
create-netflix-clone-04-server-continued.pdf
PDF
create-netflix-clone-05-client-model_transcript.pdf
PDF
create-netflix-clone-03-server_transcript.pdf
PDF
create-netflix-clone-02-server.pdf
PDF
create-netflix-clone-05-client-model.pdf
PDF
Creating a Whatsapp Clone - Part II.pdf
PDF
Creating a Whatsapp Clone - Part IX - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part II - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part V - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IV - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IV.pdf
PDF
Creating a Whatsapp Clone - Part I - Transcript.pdf
PDF
Creating a Whatsapp Clone - Part IX.pdf
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-01-introduction.pdf
create-netflix-clone-03-server.pdf
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-02-server.pdf
create-netflix-clone-05-client-model.pdf
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part IX.pdf

Recently uploaded (20)

PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PDF
Empathic Computing: Creating Shared Understanding
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
Spectroscopy.pptx food analysis technology
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Approach and Philosophy of On baking technology
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
cuic standard and advanced reporting.pdf
PPT
Teaching material agriculture food technology
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
Machine Learning_overview_presentation.pptx
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
NewMind AI Weekly Chronicles - August'25-Week II
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Assigned Numbers - 2025 - Bluetooth® Document
Empathic Computing: Creating Shared Understanding
Unlocking AI with Model Context Protocol (MCP)
Spectroscopy.pptx food analysis technology
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Approach and Philosophy of On baking technology
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
cuic standard and advanced reporting.pdf
Teaching material agriculture food technology
Digital-Transformation-Roadmap-for-Companies.pptx
Chapter 3 Spatial Domain Image Processing.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Machine Learning_overview_presentation.pptx
MYSQL Presentation for SQL database connectivity
Programs and apps: productivity, graphics, security and other tools
The Rise and Fall of 3GPP – Time for a Sabbatical?
Per capita expenditure prediction using model stacking based on satellite ima...
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx

Creating an Uber Clone - Part IX - Transcript.pdf

  • 1. Creating an Uber Clone - Part IX The “Where to?” UI is a pretty big piece of the puzzle and it also includes the navigation UI which is the bottom portion
  • 2. Destination UI © Codename One 2017 all rights reserved Up until now we focused on this part of the toolbar area
  • 3. Destination UI © Codename One 2017 all rights reserved Now we need to do this portion. The destination UI toggle is a huge part of the navigation UI. It's the bottom section of the form that contains the list of destinations. We can build it on top of the code we wrote for the navigation UI and place it in the center of the layer so it will play nicely with the rest of the UI.
  • 4. Destination UI © Codename One 2017 all rights reserved For now I’ll ignore this potion as it’s mostly a specialization of other things and can be done relatively easily if you understand the rest of the things I did
  • 5. private void showToNavigationBar(Container parentLayer) { MultiButton addHome = new MultiButton("Add Home"); addHome.setUIID("Container"); addHome.setUIIDLine1("WhereToButtonLine1"); addHome.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(addHome, FontImage.MATERIAL_HOME); MultiButton addWork = new MultiButton("Add Work"); addWork.setUIID("Container"); addWork.setUIIDLine1("WhereToButtonLine1"); addWork.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(addWork, FontImage.MATERIAL_WORK); MultiButton savedPlaces = new MultiButton("Saved Places"); savedPlaces.setUIID("Container"); savedPlaces.setUIIDLine1("WhereToButtonLineNoBorder"); savedPlaces.setEmblemUIID("WhereToButtonLineNoBorder"); savedPlaces.setIconUIID("WhereToButtonIcon"); savedPlaces.setEmblem(FontImage.createMaterial( FontImage.MATERIAL_NAVIGATE_NEXT, savedPlaces.getIconComponent().getUnselectedStyle())); FontImage.setMaterialIcon(savedPlaces, FontImage.MATERIAL_STAR_BORDER); Destination UI Let’s jump right in to the “showNavigationBar” method. The top elements are relatively simple multi-buttons, we use Container as their UIID so they will be transparent with 0 padding/margin.
  • 6. savedPlaces.setEmblem(FontImage.createMaterial( FontImage.MATERIAL_NAVIGATE_NEXT, savedPlaces.getIconComponent().getUnselectedStyle())); FontImage.setMaterialIcon(savedPlaces, FontImage.MATERIAL_STAR_BORDER); Label whereSeparator = new Label("", "WhereSeparator"); whereSeparator.setShowEvenIfBlank(true); MultiButton history1 = new MultiButton("Mikve Yisrael Str..."); history1.setUIID("Container"); history1.setUIIDLine1("WhereToButtonLine1"); history1.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY); Container result = BoxLayout.encloseY(addHome, addWork, savedPlaces, whereSeparator, history1); result.setUIID("Form"); result.setScrollableY(true); result.setScrollVisible(false); result.setY(getDisplayHeight()); Destination UI The separator is just a label with a specific style. Notice that blank labels/buttons etc. are hidden by default in Codename One and you should invoke setShowEvenIfBlank if you want such a label to still render
  • 7. savedPlaces.setEmblem(FontImage.createMaterial( FontImage.MATERIAL_NAVIGATE_NEXT, savedPlaces.getIconComponent().getUnselectedStyle())); FontImage.setMaterialIcon(savedPlaces, FontImage.MATERIAL_STAR_BORDER); Label whereSeparator = new Label("", "WhereSeparator"); whereSeparator.setShowEvenIfBlank(true); MultiButton history1 = new MultiButton("Mikve Yisrael Str..."); history1.setUIID("Container"); history1.setUIIDLine1("WhereToButtonLine1"); history1.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY); Container result = BoxLayout.encloseY(addHome, addWork, savedPlaces, whereSeparator, history1); result.setUIID("Form"); result.setScrollableY(true); result.setScrollVisible(false); result.setY(getDisplayHeight()); Destination UI We can reuse the Form UIID here because that's effectively what we want. We want this UI to appear as if it's a Form
  • 8. savedPlaces.setEmblem(FontImage.createMaterial( FontImage.MATERIAL_NAVIGATE_NEXT, savedPlaces.getIconComponent().getUnselectedStyle())); FontImage.setMaterialIcon(savedPlaces, FontImage.MATERIAL_STAR_BORDER); Label whereSeparator = new Label("", "WhereSeparator"); whereSeparator.setShowEvenIfBlank(true); MultiButton history1 = new MultiButton("Mikve Yisrael Str..."); history1.setUIID("Container"); history1.setUIIDLine1("WhereToButtonLine1"); history1.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY); Container result = BoxLayout.encloseY(addHome, addWork, savedPlaces, whereSeparator, history1); result.setUIID("Form"); result.setScrollableY(true); result.setScrollVisible(false); result.setY(getDisplayHeight()); Destination UI We need this to be scrollable but we don't want the scrollbar on the side as it might cause aesthetic issues
  • 9. Label whereSeparator = new Label("", "WhereSeparator"); whereSeparator.setShowEvenIfBlank(true); MultiButton history1 = new MultiButton("Mikve Yisrael Str..."); history1.setUIID("Container"); history1.setUIIDLine1("WhereToButtonLine1"); history1.setIconUIID("WhereToButtonIcon"); FontImage.setMaterialIcon(history1, FontImage.MATERIAL_HISTORY); Container result = BoxLayout.encloseY(addHome, addWork, savedPlaces, whereSeparator, history1); result.setUIID("Form"); result.setScrollableY(true); result.setScrollVisible(false); result.setY(getDisplayHeight()); result.setWidth(getDisplayWidth()); result.setHeight(result.getPreferredH()); parentLayer.add(CENTER, result); parentLayer.animateLayout(200); } Destination UI The showing of this element is animated from the bottom of the Form
  • 10. WhereToButtonLine1 © Codename One 2017 all rights reserved While this UI is very simple it did define a few UIID's. Let's start with WhereToButtonLine1 which represents the entries in the list of elements and also adds the underline. The color is just black over transparent which in this case leads to white
  • 11. WhereToButtonLine1 © Codename One 2017 all rights reserved The padding on the left side is relatively low since the icon will take the extra padding. On the other sides we have typical 4 millimeter padding
  • 12. WhereToButtonLine1 © Codename One 2017 all rights reserved Margin is 0 as usual
  • 13. WhereToButtonLine1 © Codename One 2017 all rights reserved We put the underline here because the design placed the underline only under the text and didn't place it under the icon which has a different UIID. The underline is a gray 2 pixel high line
  • 14. WhereToButtonLine1 © Codename One 2017 all rights reserved The font is the standard 3 millimeter light font
  • 15. WhereToButtonIcon © Codename One 2017 all rights reserved The WhereToButtonIcon style applies to the icon which has less horizontal padding so it won’t drift too far from the text but identical vertical padding so it will align properly with the text
  • 16. WhereToButtonIcon © Codename One 2017 all rights reserved It derives from WhereToButtonLine1 so they will fit together well
  • 17. WhereToButtonNoBorder © Codename One 2017 all rights reserved We need the no border version of the style so it will remove the underline border on the last entry. Otherwise we can see an out of place underline in that one last entry in the list
  • 18. WhereToButtonNoBorder © Codename One 2017 all rights reserved We derive from the same style so everything else is identical
  • 19. WhereSeparator © Codename One 2017 all rights reserved The WhereSeparator is just a gray padded line, so it has the right gray color and is completely opaque with no background transparency.
  • 20. WhereSeparator © Codename One 2017 all rights reserved It’s exactly 2 millimeters tall so it will stand out but won’t take out an entire line.
  • 21. WhereSeparator © Codename One 2017 all rights reserved Margin is 0 so it can reach the edge of the parent container
  • 22. from.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(square); if(layer.getComponentCount() > 1) { Component c = layer.getComponentAt(1); c.setY(getDisplayHeight()); layer.animateUnlayout(200, 150, () -> { c.remove(); revalidate(); }); } } public void focusLost(Component cmp) { fromSelected.setIcon(circle); } }); to.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(circle); toSelected.setIcon(square); showToNavigationBar(layer); } public void focusLost(Component cmp) { toSelected.setIcon(circle); } }); Focus Listener Now that we added this we need to show this UI and hide it when the user toggles the focus in the text fields. We can do this by binding focus listeners to the to and from text fields. When focus is lost/gained we toggle between the square and circle modes by setting the icon to the appropriate labels
  • 23. from.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(square); if(layer.getComponentCount() > 1) { Component c = layer.getComponentAt(1); c.setY(getDisplayHeight()); layer.animateUnlayout(200, 150, () -> { c.remove(); revalidate(); }); } } public void focusLost(Component cmp) { fromSelected.setIcon(circle); } }); to.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(circle); toSelected.setIcon(square); showToNavigationBar(layer); } public void focusLost(Component cmp) { toSelected.setIcon(circle); } }); Focus Listener We always have one container in the layer except for the case where the second component is the where to container. It's always the second component because it's always added last.
  • 24. from.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(square); if(layer.getComponentCount() > 1) { Component c = layer.getComponentAt(1); c.setY(getDisplayHeight()); layer.animateUnlayout(200, 150, () -> { c.remove(); revalidate(); }); } } public void focusLost(Component cmp) { fromSelected.setIcon(circle); } }); to.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(circle); toSelected.setIcon(square); showToNavigationBar(layer); } public void focusLost(Component cmp) { toSelected.setIcon(circle); } }); Focus Listener We set the position of this container below the forms
  • 25. from.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(square); if(layer.getComponentCount() > 1) { Component c = layer.getComponentAt(1); c.setY(getDisplayHeight()); layer.animateUnlayout(200, 150, () -> { c.remove(); revalidate(); }); } } public void focusLost(Component cmp) { fromSelected.setIcon(circle); } }); to.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(circle); toSelected.setIcon(square); showToNavigationBar(layer); } public void focusLost(Component cmp) { toSelected.setIcon(circle); } }); Focus Listener Animate "unlayout" moves the component outside of the screen to the position we asked for using a smooth animation
  • 26. from.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(square); if(layer.getComponentCount() > 1) { Component c = layer.getComponentAt(1); c.setY(getDisplayHeight()); layer.animateUnlayout(200, 150, () -> { c.remove(); revalidate(); }); } } public void focusLost(Component cmp) { fromSelected.setIcon(circle); } }); to.addFocusListener(new FocusListener() { public void focusGained(Component cmp) { fromSelected.setIcon(circle); toSelected.setIcon(square); showToNavigationBar(layer); } public void focusLost(Component cmp) { toSelected.setIcon(circle); } }); Focus Listener This callback is invoked when the unlayout completes. At this point we have an invalid UI that needs a layout but before we do that we remove the component that we animated out of the form
  • 27. back.addActionListener(e -> { navigationToolbar.setY(-navigationToolbar.getHeight()); if(layer.getComponentCount() > 1) { layer.getComponentAt(1).setY(getDisplayHeight()); } navigationToolbar.getParent().animateUnlayout(200, 120, () -> { layer.removeAll(); revalidate(); }); }); Back Now that the UI appears we also need to remove it when going back so I'll update the back action listener from above to handle the "Where to?" UI as well. This is the exact same unlayout operation we did before
  • 28. navigationToolbar.getUnselectedStyle().setBgPainter((g1, rect) -> { g1.setAlpha(255); g1.setColor(0xffffff); if(dropShadow != null) { if(layer.getComponentCount() > 1) { g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); } g1.drawImage(dropShadow, rect.getX(), rect.getY() + rect.getHeight() - dropShadow.getHeight(), rect.getWidth(), dropShadow.getHeight()); g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight() - dropShadow.getHeight() / 2); } else { g1.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); } g1.setColor(0xa4a4ac); g1.setAntiAliased(true); int x = fromSelected.getAbsoluteX() + fromSelected.getWidth() / 2 - 1; int y = fromSelected.getAbsoluteY() + fromSelected.getHeight() / 2 + circle.getHeight() / 2; g1.fillRect(x, y, 2, toSelected.getAbsoluteY() - y + toSelected.getHeight() / 2 - circle.getHeight() / 2); }); Background Painter And finally we need to make a subtle but important change to the background painter code from before. Because of the drop shadow a gap is formed between the top and bottom pieces so a special case here paints a white rectangle under the shadow to hide the gap. Without that the shadow would appear on top of the map and not on top of a white background. Once this is done opening the "Where To?" UI and toggling the fields should work as expected.