SlideShare a Scribd company logo
Creating an Uber Clone - Part VII
In this section we finally get to “the map” UI, I hope you followed the instructions before for configuring the map. If not please follow through with that if things don’t work
and if you don’t see the map or it acts funny check out with us in the online support forums. You can also check out the map section in the deep dive into mobile
development course which comes bundled.

Before we begin we need a class we discussed before in the maps module. The MapLayout, I didn’t change much as left it “as is”. Since it’s already available elsewhere I
won’t go into the full discussion here and go right into the form itself.
The Map
© Codename One 2017 all rights reserved
Before we proceed I'd like to highlight some subtle things in this screenshot and discuss what we will and will not do.
The Map
© Codename One 2017 all rights reserved
✦The hamburger menu button above
represents the side menu
✦The "Where to?" field is really a button
✦For now I'll just place one unmoving Taxi
and we'll deal with the positioning later on
✦The two icons on the bottom represent
historic rides which I'll hardcode for now
✦The bottom notice is something I won't
implement within the app
I won’t do the side menu right now but I will do it soon…

The "Where to?" field is really a button that leads to a different UI. This UI is overlaid on top of the map so it is a part of this Form

I’ll position one taxi in a hardcoded location as part of the mockup

The icons at the bottom are historic rides, I’ll add two hardcoded historic rides for now

I won’t go into the notice at the bottom. It's possible but it would be non-trivial
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
Lets jump right into the code.

You need the JS key from Google Maps as explained in the map extension page. This must have a filled up value
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
We usually use BorderLayout which implicitly disables scrollability. LayeredLayout doesn't do that and the forms content pane is scrollable on the Y-axis by default
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
Notice we didn't use a thread here and instead used the callSeriallyOnIdle method. On the login Form I didn't use that because the animation might have prevented idle
from occurring. This shadow is used later on in the showNavigationToolbar method
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
The transitions in the main application are based on cover and the transition out will only be a problem
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
The map is on the lowest layer and everything is placed on top of it
public class MapForm extends Form {
private static final String MAP_JS_KEY = "AIza-------";
private Image square;
private Image dropShadow;
public MapForm() {
super(new LayeredLayout());
setScrollableY(false);
Display.getInstance().callSeriallyOnIdle(() -> {
dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30,
convertToPixels(3), 0.40f);
});
setTransitionOutAnimator(CommonTransitions.createEmpty());
MapContainer mc = new MapContainer(MAP_JS_KEY);
mc.setShowMyLocation(true);
add(mc);
Container mapLayer = new Container();
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
MapForm
This layer is on top of the map and uses the MapLayout. Here we will place the car and other landmarks we need
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
mapLayer.add(telAviv, car);
square = Image.createImage(convertToPixels(0.7f),
convertToPixels(0.7f), 0xff000000);
Button whereTo = new Button("Where To?", square, "WhereTo");
whereTo.setGap(convertToPixels(3));
add(BoxLayout.encloseY(whereTo));
FloatingActionButton history1 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
FloatingActionButton history2 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4);
TextArea history2Label = new TextArea("Burgeranch", 3, 4);
history1Label.setUIID("HistoryLabel");
history2Label.setUIID("HistoryLabel");
history1Label.setEditable(false);
MapForm
I place a car on top of the map in Tel Aviv. Notice that the car is just a label... I've set the opacity to 140 to match the translucent cars in the native app. Notice that the
`MapLayout` takes a `Coord` as a constraint so it can properly position the car…
mapLayer.setLayout(new MapLayout(mc, mapLayer));
add(mapLayer);
Coord telAviv = new Coord(32.072449, 34.778613);
mc.zoom(telAviv, mc.getMaxZoom() + 1);
Label car = new Label(Resources.getGlobalResources().
getImage("map-vehicle-icon-uberX.png"));
car.getAllStyles().setOpacity(140);
mapLayer.add(telAviv, car);
square = Image.createImage(convertToPixels(0.7f),
convertToPixels(0.7f), 0xff000000);
Button whereTo = new Button("Where To?", square, "WhereTo");
whereTo.setGap(convertToPixels(3));
add(BoxLayout.encloseY(whereTo));
FloatingActionButton history1 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
FloatingActionButton history2 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4);
TextArea history2Label = new TextArea("Burgeranch", 3, 4);
history1Label.setUIID("HistoryLabel");
history2Label.setUIID("HistoryLabel");
history1Label.setEditable(false);
MapForm
This is the small square we place next to the "Where to?" button. I could have used a unicode value too but it wasn't available in all the fonts. Notice the "Where to?"
element is just a button as it moves us to a separate UI and isn't really a TextField
whereTo.setGap(convertToPixels(3));
add(BoxLayout.encloseY(whereTo));
FloatingActionButton history1 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
FloatingActionButton history2 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4);
TextArea history2Label = new TextArea("Burgeranch", 3, 4);
history1Label.setUIID("HistoryLabel");
history2Label.setUIID("HistoryLabel");
history1Label.setEditable(false);
history1Label.setGrowByContent(false);
history2Label.setEditable(false);
history2Label.setGrowByContent(false);
Container h1 = BoxLayout.encloseY(history1, history1Label);
Container h2 = BoxLayout.encloseY(history2, history2Label);
h1.setLeadComponent(history1);
h2.setLeadComponent(history2);
ScaleImageLabel gradient = new ScaleImageLabel(Resources.
getGlobalResources().getImage("gradient-overlay.png"));
gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
add(BorderLayout.south(gradient));
MapForm
The history buttons are FloatingActionButton instances that are customized in terms of styling. I used TextArea instead of SpanLabel because I wanted the history
element to act as a single component with lead component. Lead components can take over a hierarchy of several components and handle the events for everyone so in
this case a click on the text area below the history will trigger an event in the FloatingActionButton
whereTo.setGap(convertToPixels(3));
add(BoxLayout.encloseY(whereTo));
FloatingActionButton history1 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
FloatingActionButton history2 = FloatingActionButton.createFAB(
FontImage.MATERIAL_HISTORY, "History");
TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4);
TextArea history2Label = new TextArea("Burgeranch", 3, 4);
history1Label.setUIID("HistoryLabel");
history2Label.setUIID("HistoryLabel");
history1Label.setEditable(false);
history1Label.setGrowByContent(false);
history2Label.setEditable(false);
history2Label.setGrowByContent(false);
Container h1 = BoxLayout.encloseY(history1, history1Label);
Container h2 = BoxLayout.encloseY(history2, history2Label);
h1.setLeadComponent(history1);
h2.setLeadComponent(history2);
ScaleImageLabel gradient = new ScaleImageLabel(Resources.
getGlobalResources().getImage("gradient-overlay.png"));
gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
add(BorderLayout.south(gradient));
MapForm
The bottom of the map has a gradient overlay that darkens the bottom. This is probably in place to make the history labels readable. I just generated a gradient image in
photoshop and placed it there
history1Label.setUIID("HistoryLabel");
history2Label.setUIID("HistoryLabel");
history1Label.setEditable(false);
history1Label.setGrowByContent(false);
history2Label.setEditable(false);
history2Label.setGrowByContent(false);
Container h1 = BoxLayout.encloseY(history1, history1Label);
Container h2 = BoxLayout.encloseY(history2, history2Label);
h1.setLeadComponent(history1);
h2.setLeadComponent(history2);
ScaleImageLabel gradient = new ScaleImageLabel(Resources.
getGlobalResources().getImage("gradient-overlay.png"));
gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
add(BorderLayout.south(gradient));
add(BorderLayout.south(FlowLayout.encloseCenter(h1, h2)));
}
@Override
protected void initGlobalToolbar() {
setToolbar(new Toolbar(true));
CommonCode.constructSideMenu(getToolbar());
}
}
MapForm
We do two important things here... We use the overlay toolbar which "floats" on top of the UI. We initialize the side menu which we will discuss soon…

That was a lot to cover but there is a lot more... We did mention 3 new styles above which isn't that much all things considered.
WhereTo
© Codename One 2017 all rights reserved
The WhereTo style has some subtle nuances such as dark gray text
WhereTo
© Codename One 2017 all rights reserved
The padding is large and obvious, I played with it a bit to get it right
WhereTo
© Codename One 2017 all rights reserved
The margin is special, we want some margin from the sides so it won’t touch them. We need a lot of margin from the top to leave room for the title area
WhereTo
© Codename One 2017 all rights reserved
The corners are rounded on the native widget, this is very subtle so I left it at 0.3 millimeters which should be very easy. It also has a shadow which is more obvious and
80 opacity
WhereTo
© Codename One 2017 all rights reserved
The font isn’t big just slightly bigger than normal but the typical light font
History
© Codename One 2017 all rights reserved
The History button is the round button on the bottom of the map leading to historic rides. It’s black on white but is implemented as a floating action button
HistoryLabel
© Codename One 2017 all rights reserved
So it derives from that and uses the border settings from the floating action button
HistoryLabel
© Codename One 2017 all rights reserved
The history label is the text element below which is technically a text area but acts as a label. The text color for this is black
HistoryLabel
© Codename One 2017 all rights reserved
While the padding is 2 millimeters on all sides except for the top where we want to be as close as possible to the floating action button which is already well padded
HistoryLabel
© Codename One 2017 all rights reserved
Margin is zero as usual
HistoryLabel
© Codename One 2017 all rights reserved
And the font is relatively small at 2.2 millimeters so we can fit multiple rides in the form

More Related Content

PDF
Creating an Uber Clone - Part VII.pdf
PDF
Creating an Uber Clone - Part XXI - Transcript.pdf
PDF
Creating an Uber Clone - Part VIII - Transcript.pdf
PDF
Creating an Uber Clone - Part XXIII - Transcript.pdf
PDF
Creating an Uber Clone - Part XXI.pdf
PDF
Creating an Uber Clone - Part XXIII.pdf
PDF
Creating an Uber Clone - Part XXX - Transcript.pdf
PDF
Creating an Uber Clone - Part XVI.pdf
Creating an Uber Clone - Part VII.pdf
Creating an Uber Clone - Part XXI - Transcript.pdf
Creating an Uber Clone - Part VIII - Transcript.pdf
Creating an Uber Clone - Part XXIII - Transcript.pdf
Creating an Uber Clone - Part XXI.pdf
Creating an Uber Clone - Part XXIII.pdf
Creating an Uber Clone - Part XXX - Transcript.pdf
Creating an Uber Clone - Part XVI.pdf

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

PDF
Adapting to Tablets and Desktops - Part 3 - Transcript.pdf
PDF
Creating an Uber Clone - Part XXII - Transcript.pdf
PDF
Creating an Uber Clone - Part VIII.pdf
PDF
Creating an Uber Clone - Part XX - Transcript.pdf
PDF
Creating an Uber Clone - Part XXX.pdf
PDF
Initial UI Mockup - Part 2 - Transcript.pdf
PDF
Maps - Part 2.pdf
PDF
Maps - Part 3 - Transcript.pdf
PPTX
Android chapter25-map views
PDF
Creating a Facebook Clone - Part XXXVI - Transcript.pdf
PDF
Creating an Uber Clone - Part V - Transcript.pdf
PPTX
Android MapView and MapActivity
PDF
Initial UI Mockup - Part 2.pdf
PDF
Designing User Interface for Mobile Content in Flash lite
PPTX
Adobe MAX 2009: Making Maps with Flash
KEY
SmartphoneKanto#10
PDF
Creating an Uber Clone - Part XXII.pdf
PDF
Creating an Uber Clone - Part XIX - Transcript.pdf
PDF
Creating an Uber Clone - Part IX - Transcript.pdf
Adapting to Tablets and Desktops - Part 3 - Transcript.pdf
Creating an Uber Clone - Part XXII - Transcript.pdf
Creating an Uber Clone - Part VIII.pdf
Creating an Uber Clone - Part XX - Transcript.pdf
Creating an Uber Clone - Part XXX.pdf
Initial UI Mockup - Part 2 - Transcript.pdf
Maps - Part 2.pdf
Maps - Part 3 - Transcript.pdf
Android chapter25-map views
Creating a Facebook Clone - Part XXXVI - Transcript.pdf
Creating an Uber Clone - Part V - Transcript.pdf
Android MapView and MapActivity
Initial UI Mockup - Part 2.pdf
Designing User Interface for Mobile Content in Flash lite
Adobe MAX 2009: Making Maps with Flash
SmartphoneKanto#10
Creating an Uber Clone - Part XXII.pdf
Creating an Uber Clone - Part XIX - Transcript.pdf
Creating an Uber Clone - Part IX - 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-06-client-ui_transcript.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
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-06-client-ui_transcript.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

Recently uploaded (20)

PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
Spectroscopy.pptx food analysis technology
PDF
Approach and Philosophy of On baking technology
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Reach Out and Touch Someone: Haptics and Empathic Computing
NewMind AI Weekly Chronicles - August'25-Week II
Spectral efficient network and resource selection model in 5G networks
Per capita expenditure prediction using model stacking based on satellite ima...
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Spectroscopy.pptx food analysis technology
Approach and Philosophy of On baking technology
Chapter 3 Spatial Domain Image Processing.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Digital-Transformation-Roadmap-for-Companies.pptx
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
“AI and Expert System Decision Support & Business Intelligence Systems”
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Encapsulation_ Review paper, used for researhc scholars
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Mobile App Security Testing_ A Comprehensive Guide.pdf

Creating an Uber Clone - Part VII - Transcript.pdf

  • 1. Creating an Uber Clone - Part VII In this section we finally get to “the map” UI, I hope you followed the instructions before for configuring the map. If not please follow through with that if things don’t work and if you don’t see the map or it acts funny check out with us in the online support forums. You can also check out the map section in the deep dive into mobile development course which comes bundled. Before we begin we need a class we discussed before in the maps module. The MapLayout, I didn’t change much as left it “as is”. Since it’s already available elsewhere I won’t go into the full discussion here and go right into the form itself.
  • 2. The Map © Codename One 2017 all rights reserved Before we proceed I'd like to highlight some subtle things in this screenshot and discuss what we will and will not do.
  • 3. The Map © Codename One 2017 all rights reserved ✦The hamburger menu button above represents the side menu ✦The "Where to?" field is really a button ✦For now I'll just place one unmoving Taxi and we'll deal with the positioning later on ✦The two icons on the bottom represent historic rides which I'll hardcode for now ✦The bottom notice is something I won't implement within the app I won’t do the side menu right now but I will do it soon… The "Where to?" field is really a button that leads to a different UI. This UI is overlaid on top of the map so it is a part of this Form I’ll position one taxi in a hardcoded location as part of the mockup The icons at the bottom are historic rides, I’ll add two hardcoded historic rides for now I won’t go into the notice at the bottom. It's possible but it would be non-trivial
  • 4. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm Lets jump right into the code. You need the JS key from Google Maps as explained in the map extension page. This must have a filled up value
  • 5. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm We usually use BorderLayout which implicitly disables scrollability. LayeredLayout doesn't do that and the forms content pane is scrollable on the Y-axis by default
  • 6. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm Notice we didn't use a thread here and instead used the callSeriallyOnIdle method. On the login Form I didn't use that because the animation might have prevented idle from occurring. This shadow is used later on in the showNavigationToolbar method
  • 7. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm The transitions in the main application are based on cover and the transition out will only be a problem
  • 8. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm The map is on the lowest layer and everything is placed on top of it
  • 9. public class MapForm extends Form { private static final String MAP_JS_KEY = "AIza-------"; private Image square; private Image dropShadow; public MapForm() { super(new LayeredLayout()); setScrollableY(false); Display.getInstance().callSeriallyOnIdle(() -> { dropShadow = LoginForm.squareShadow(getDisplayWidth(), 30, convertToPixels(3), 0.40f); }); setTransitionOutAnimator(CommonTransitions.createEmpty()); MapContainer mc = new MapContainer(MAP_JS_KEY); mc.setShowMyLocation(true); add(mc); Container mapLayer = new Container(); mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); MapForm This layer is on top of the map and uses the MapLayout. Here we will place the car and other landmarks we need
  • 10. mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); mapLayer.add(telAviv, car); square = Image.createImage(convertToPixels(0.7f), convertToPixels(0.7f), 0xff000000); Button whereTo = new Button("Where To?", square, "WhereTo"); whereTo.setGap(convertToPixels(3)); add(BoxLayout.encloseY(whereTo)); FloatingActionButton history1 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); FloatingActionButton history2 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4); TextArea history2Label = new TextArea("Burgeranch", 3, 4); history1Label.setUIID("HistoryLabel"); history2Label.setUIID("HistoryLabel"); history1Label.setEditable(false); MapForm I place a car on top of the map in Tel Aviv. Notice that the car is just a label... I've set the opacity to 140 to match the translucent cars in the native app. Notice that the `MapLayout` takes a `Coord` as a constraint so it can properly position the car…
  • 11. mapLayer.setLayout(new MapLayout(mc, mapLayer)); add(mapLayer); Coord telAviv = new Coord(32.072449, 34.778613); mc.zoom(telAviv, mc.getMaxZoom() + 1); Label car = new Label(Resources.getGlobalResources(). getImage("map-vehicle-icon-uberX.png")); car.getAllStyles().setOpacity(140); mapLayer.add(telAviv, car); square = Image.createImage(convertToPixels(0.7f), convertToPixels(0.7f), 0xff000000); Button whereTo = new Button("Where To?", square, "WhereTo"); whereTo.setGap(convertToPixels(3)); add(BoxLayout.encloseY(whereTo)); FloatingActionButton history1 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); FloatingActionButton history2 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4); TextArea history2Label = new TextArea("Burgeranch", 3, 4); history1Label.setUIID("HistoryLabel"); history2Label.setUIID("HistoryLabel"); history1Label.setEditable(false); MapForm This is the small square we place next to the "Where to?" button. I could have used a unicode value too but it wasn't available in all the fonts. Notice the "Where to?" element is just a button as it moves us to a separate UI and isn't really a TextField
  • 12. whereTo.setGap(convertToPixels(3)); add(BoxLayout.encloseY(whereTo)); FloatingActionButton history1 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); FloatingActionButton history2 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4); TextArea history2Label = new TextArea("Burgeranch", 3, 4); history1Label.setUIID("HistoryLabel"); history2Label.setUIID("HistoryLabel"); history1Label.setEditable(false); history1Label.setGrowByContent(false); history2Label.setEditable(false); history2Label.setGrowByContent(false); Container h1 = BoxLayout.encloseY(history1, history1Label); Container h2 = BoxLayout.encloseY(history2, history2Label); h1.setLeadComponent(history1); h2.setLeadComponent(history2); ScaleImageLabel gradient = new ScaleImageLabel(Resources. getGlobalResources().getImage("gradient-overlay.png")); gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); add(BorderLayout.south(gradient)); MapForm The history buttons are FloatingActionButton instances that are customized in terms of styling. I used TextArea instead of SpanLabel because I wanted the history element to act as a single component with lead component. Lead components can take over a hierarchy of several components and handle the events for everyone so in this case a click on the text area below the history will trigger an event in the FloatingActionButton
  • 13. whereTo.setGap(convertToPixels(3)); add(BoxLayout.encloseY(whereTo)); FloatingActionButton history1 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); FloatingActionButton history2 = FloatingActionButton.createFAB( FontImage.MATERIAL_HISTORY, "History"); TextArea history1Label = new TextArea("Mikve Yisrael Str...", 3, 4); TextArea history2Label = new TextArea("Burgeranch", 3, 4); history1Label.setUIID("HistoryLabel"); history2Label.setUIID("HistoryLabel"); history1Label.setEditable(false); history1Label.setGrowByContent(false); history2Label.setEditable(false); history2Label.setGrowByContent(false); Container h1 = BoxLayout.encloseY(history1, history1Label); Container h2 = BoxLayout.encloseY(history2, history2Label); h1.setLeadComponent(history1); h2.setLeadComponent(history2); ScaleImageLabel gradient = new ScaleImageLabel(Resources. getGlobalResources().getImage("gradient-overlay.png")); gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); add(BorderLayout.south(gradient)); MapForm The bottom of the map has a gradient overlay that darkens the bottom. This is probably in place to make the history labels readable. I just generated a gradient image in photoshop and placed it there
  • 14. history1Label.setUIID("HistoryLabel"); history2Label.setUIID("HistoryLabel"); history1Label.setEditable(false); history1Label.setGrowByContent(false); history2Label.setEditable(false); history2Label.setGrowByContent(false); Container h1 = BoxLayout.encloseY(history1, history1Label); Container h2 = BoxLayout.encloseY(history2, history2Label); h1.setLeadComponent(history1); h2.setLeadComponent(history2); ScaleImageLabel gradient = new ScaleImageLabel(Resources. getGlobalResources().getImage("gradient-overlay.png")); gradient.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); add(BorderLayout.south(gradient)); add(BorderLayout.south(FlowLayout.encloseCenter(h1, h2))); } @Override protected void initGlobalToolbar() { setToolbar(new Toolbar(true)); CommonCode.constructSideMenu(getToolbar()); } } MapForm We do two important things here... We use the overlay toolbar which "floats" on top of the UI. We initialize the side menu which we will discuss soon… That was a lot to cover but there is a lot more... We did mention 3 new styles above which isn't that much all things considered.
  • 15. WhereTo © Codename One 2017 all rights reserved The WhereTo style has some subtle nuances such as dark gray text
  • 16. WhereTo © Codename One 2017 all rights reserved The padding is large and obvious, I played with it a bit to get it right
  • 17. WhereTo © Codename One 2017 all rights reserved The margin is special, we want some margin from the sides so it won’t touch them. We need a lot of margin from the top to leave room for the title area
  • 18. WhereTo © Codename One 2017 all rights reserved The corners are rounded on the native widget, this is very subtle so I left it at 0.3 millimeters which should be very easy. It also has a shadow which is more obvious and 80 opacity
  • 19. WhereTo © Codename One 2017 all rights reserved The font isn’t big just slightly bigger than normal but the typical light font
  • 20. History © Codename One 2017 all rights reserved The History button is the round button on the bottom of the map leading to historic rides. It’s black on white but is implemented as a floating action button
  • 21. HistoryLabel © Codename One 2017 all rights reserved So it derives from that and uses the border settings from the floating action button
  • 22. HistoryLabel © Codename One 2017 all rights reserved The history label is the text element below which is technically a text area but acts as a label. The text color for this is black
  • 23. HistoryLabel © Codename One 2017 all rights reserved While the padding is 2 millimeters on all sides except for the top where we want to be as close as possible to the floating action button which is already well padded
  • 24. HistoryLabel © Codename One 2017 all rights reserved Margin is zero as usual
  • 25. HistoryLabel © Codename One 2017 all rights reserved And the font is relatively small at 2.2 millimeters so we can fit multiple rides in the form