SlideShare a Scribd company logo
1 of 18
Download to read offline
Creating an Uber Clone - Part XIX
Now that the basic "infrastructure" is out of the way we'll start wiring it into the UI starting with search. The initial search UI I did in the mockup was very "naive". It just
toggled the completion on and off.
Search UI
✦Swipe
✦Completion
✦Map location picking
© Codename One 2017 all rights reserved
The real Uber app allows us to swipe the search UI down and then pick a location in the map using a pin. This then updates the text field with the selected location.
Alternatively, if you type into the text field locations are suggested to you as you type.

There is a great deal of nuanced behavior in this UI so I only focused on the "big ticket" features. 

The first big feature is the swipe down feature which allows us to swipe down the completion UI as we type.

The second is the actual completion UI where elements update as we type.

And finally the ability to point at a location on the Map with a pin and select that location. 

I didn't implement a lot of the relatively easy features such as bookmarked locations or history in the search UI. Those should be trivial to fill in. 

In order to implement these I moved most of the UI logic into separate classes specifically AutoCompleteAddressInput & CompletionContainer both of which I’ll discuss
shortly
Search Completion
© Codename One 2017 all rights reserved
A major feature of AutoCompleteAddressInput is its ability to fold/unfold the CompletionContainer. It accomplishes this by binding pointer listeners to the parent form and
using them to implement the drag and drop behavior.

On the left you can see the search UI with completion suggestions appearing below.

When the suggestions are dragged down we can pick the location from the map as you can see on the right. As the map is dragged the location name is updated into
the text field
public class AutoCompleteAddressInput extends TextField {
private final Container layers;
private int firstX = -1, firstY = -1;
private boolean dragStarted;
private CompletionContainer completion;
private ActionListener<ActionEvent> dragListener, releaseListener;
private Location currentLocation;
private boolean blockChangeEvent;
public AutoCompleteAddressInput(String value, String hint,
Container layers, CompletionContainer completion) {
super(value, hint, 40, TextField.ANY);
this.completion = completion;
this.layers = layers;
getHintLabel().setUIID("FromToTextFieldHint");
setUIID("FromToTextField");
addDataChangedListener((i, ii) -> {
if(blockChangeEvent) {
return;
}
if(!getText().equals(value)) {
completion.updateCompletion(getText(), this);
}
AutoCompleteAddressInput
I refactored some of the code from the MapForm class into the AutoCompleteAddressInput class. It made it easier to implement some of the related logic.

I chose to derive TextField rather than encapsulate it mostly due to convenience. Encapsulation would have worked just as well for this case
public class AutoCompleteAddressInput extends TextField {
private final Container layers;
private int firstX = -1, firstY = -1;
private boolean dragStarted;
private CompletionContainer completion;
private ActionListener<ActionEvent> dragListener, releaseListener;
private Location currentLocation;
private boolean blockChangeEvent;
public AutoCompleteAddressInput(String value, String hint,
Container layers, CompletionContainer completion) {
super(value, hint, 40, TextField.ANY);
this.completion = completion;
this.layers = layers;
getHintLabel().setUIID("FromToTextFieldHint");
setUIID("FromToTextField");
addDataChangedListener((i, ii) -> {
if(blockChangeEvent) {
return;
}
if(!getText().equals(value)) {
completion.updateCompletion(getText(), this);
}
AutoCompleteAddressInput
With the exception of these last two variables every other variable here is in the service of the drag and drop logic
public class AutoCompleteAddressInput extends TextField {
private final Container layers;
private int firstX = -1, firstY = -1;
private boolean dragStarted;
private CompletionContainer completion;
private ActionListener<ActionEvent> dragListener, releaseListener;
private Location currentLocation;
private boolean blockChangeEvent;
public AutoCompleteAddressInput(String value, String hint,
Container layers, CompletionContainer completion) {
super(value, hint, 40, TextField.ANY);
this.completion = completion;
this.layers = layers;
getHintLabel().setUIID("FromToTextFieldHint");
setUIID("FromToTextField");
addDataChangedListener((i, ii) -> {
if(blockChangeEvent) {
return;
}
if(!getText().equals(value)) {
completion.updateCompletion(getText(), this);
}
AutoCompleteAddressInput
We use the DataChangedListener to send events to the completion logic, however this callback can be very verbose and it's sometimes invoked by setText. The solution
is a special version of `setText` that blocks this callback and reduces the noise in the completion code

with the blockChangeEvent variable
addDataChangedListener((i, ii) -> {
if(blockChangeEvent) {
return;
}
if(!getText().equals(value)) {
completion.updateCompletion(getText(), this);
}
});
}
public void setTextNoEvent(String text) {
blockChangeEvent = true;
setText(text);
blockChangeEvent = false;
}
@Override
protected void focusGained() {
completion.initCompletionBar();
}
@Override
protected void deinitialize() {
AutoCompleteAddressInput
The last focused text field is the one that now handles the completion so if the user was in the to text field everything typed will now impact the completion for to and visa
versa
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
Pointer listeners on the Form allow us to detect pointer events everywhere. We bind them in the initComponent method and remove them in the deinitialize method. This
prevents a memory leak and a situation where pointer processing code keeps running and taking up CPU. deinitialize is invoked when a component is removed from the
UI or its hierarchy is removed. It's also invoked when a different Form is shown instead of the current Form
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
initComponent is invoked when a component is "there". It will be invoked if a component is added to an already showing Form or if a parent Form is shown. You can rely
on initComponent and deinitialize working in tandem. They might be invoked multiple times in valid situations for instance a Dialog shown on top of a Form triggers a
deinitialize on the components of the Form followed by an initComponent when it's disposed
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
Despite using the shorthand lambda syntax for event handling I need to keep a reference to the drag and release event objects so I can remove them later
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
The dragged element is always the second element (0 is the first). It can be dragged between the CENTER location and the SOUTH location
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
If this is indeed a drag operation we'd like to block the event from propagating onwards
protected void deinitialize() {
if(dragListener != null) {
Form f = getComponentForm();
f.removePointerDraggedListener(dragListener);
f.removePointerReleasedListener(dragListener);
}
super.deinitialize();
}
@Override
protected void initComponent() {
super.initComponent();
if(dragListener == null) {
dragListener = e -> {
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
if(dragStarted) {
e.consume();
cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS);
if(dragUp) {
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
AutoCompleteAddressInput
When a component is in the SOUTH we set its preferred size to one 8th of the display height so it won't peek up too much. When its dragged up we just increase that
size during drag
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
} else {
cmp.getUnselectedStyle().setMarginTop(
Math.max(0, e.getY() - firstY));
}
layers.revalidate();
} else {
Component draggedCmp =
getComponentForm().getComponentAt(e.getX(), e.getY());
if(!draggedCmp.isChildOf((Container)cmp)) {
return;
}
if(firstX == -1) {
firstX = e.getX();
firstY = e.getY();
}
if((!dragUp && e.getY() - firstY > convertToPixels(2)) ||
(dragUp && firstY - e.getY() > convertToPixels(2))) {
e.consume();
dragStarted = true;
}
AutoCompleteAddressInput
Components in the CENTER ignore their preferred size and take up available space so we use margin to provide the drag effect
cmp.setPreferredSize(new Dimension(getDisplayWidth(),
firstY - e.getY() + getDisplayHeight() / 8));
} else {
cmp.getUnselectedStyle().setMarginTop(
Math.max(0, e.getY() - firstY));
}
layers.revalidate();
} else {
Component draggedCmp =
getComponentForm().getComponentAt(e.getX(), e.getY());
if(!draggedCmp.isChildOf((Container)cmp)) {
return;
}
if(firstX == -1) {
firstX = e.getX();
firstY = e.getY();
}
if((!dragUp && e.getY() - firstY > convertToPixels(2)) ||
(dragUp && firstY - e.getY() > convertToPixels(2))) {
e.consume();
dragStarted = true;
}
AutoCompleteAddressInput
This prevents a drag event on a different region in the form from triggering this event. E.g. if a user drags the map
(dragUp && firstY - e.getY() > convertToPixels(2))) {
e.consume();
dragStarted = true;
}
}
};
getComponentForm().addPointerDraggedListener(dragListener);
releaseListener = e -> {
if(dragStarted) {
e.consume();
Component cmp = layers.getComponentAt(1);
boolean dragUp = layers.getLayout().
getComponentConstraint(cmp).equals(SOUTH);
cmp.remove();
cmp.setUIID(cmp.getUIID());
boolean animateDown;
if(dragUp) {
animateDown = !(firstY - e.getY() > convertToPixels(8));
} else {
animateDown = e.getY() - firstY > convertToPixels(8);
}
if(animateDown) {
layers.add(SOUTH, cmp);
AutoCompleteAddressInput
Dragging just displayed a motion. We now need to remove the component and place it where it should be. We also reset the UIID so styling changes (e.g. margin, unit
type etc.) will reset to the default
if(dragUp) {
animateDown = !(firstY - e.getY() > convertToPixels(8));
} else {
animateDown = e.getY() - firstY > convertToPixels(8);
}
if(animateDown) {
layers.add(SOUTH, cmp);
cmp.setPreferredSize(
new Dimension(getDisplayWidth(), getDisplayHeight() / 8));
Style s = cmp.getUnselectedStyle();
s.setMarginUnit(Style.UNIT_TYPE_DIPS);
s.setMarginLeft(3);
s.setMarginRight(3);
} else {
layers.add(CENTER, cmp);
cmp.setPreferredSize(null);
}
layers.animateLayout(200);
firstX = -1;
firstY = -1;
dragStarted = false;
}
};
AutoCompleteAddressInput
When we place the container in the SOUTH we set the preferred size and margin to match. When we place it in the CENTER we set the preferred size to null which is a
special case that resets previous manual settings and restores the default
s.setMarginRight(3);
} else {
layers.add(CENTER, cmp);
cmp.setPreferredSize(null);
}
layers.animateLayout(200);
firstX = -1;
firstY = -1;
dragStarted = false;
}
};
getComponentForm().addPointerReleasedListener(releaseListener);
}
}
public Location getCurrentLocation() {
return currentLocation;
}
public void setCurrentLocation(Location currentLocation) {
this.currentLocation = currentLocation;
}
}
AutoCompleteAddressInput
The location of a text field uses strings but what we really care about is coordinates on the map which is why I store them here. This is used both by the map pin logic
and by the search logic we will use later on

More Related Content

Similar to Creating an Uber Clone - Part XIX - Transcript.pdf

Creating an Uber Clone - Part IX.pdf
Creating an Uber Clone - Part IX.pdfCreating an Uber Clone - Part IX.pdf
Creating an Uber Clone - Part IX.pdfShaiAlmog1
 
Android Event and IntentAndroid Event and Intent
Android Event and IntentAndroid Event and IntentAndroid Event and IntentAndroid Event and Intent
Android Event and IntentAndroid Event and Intentadmin220812
 
Value isnt changing and I cant seem to get the conversion to wor.pdf
Value isnt changing and I cant seem to get the conversion to wor.pdfValue isnt changing and I cant seem to get the conversion to wor.pdf
Value isnt changing and I cant seem to get the conversion to wor.pdfamirthagiftsmadurai
 
This is a C# project . I am expected to create as this image shows. .pdf
This is a C# project . I am expected to create as this image shows. .pdfThis is a C# project . I am expected to create as this image shows. .pdf
This is a C# project . I am expected to create as this image shows. .pdfindiaartz
 
What's New in Android
What's New in AndroidWhat's New in Android
What's New in AndroidRobert Cooper
 
20180721 code defragment
20180721 code defragment20180721 code defragment
20180721 code defragmentChiwon Song
 
Advancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and GesturesAdvancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and GesturesSamsung Developers
 
Adapting to Tablets and Desktops - Part 1.pdf
Adapting to Tablets and Desktops - Part 1.pdfAdapting to Tablets and Desktops - Part 1.pdf
Adapting to Tablets and Desktops - Part 1.pdfShaiAlmog1
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your appsJuan C Catalan
 
F# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, HerefordF# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, HerefordKit Eason
 
Creating custom views
Creating custom viewsCreating custom views
Creating custom viewsMu Chun Wang
 
Flutter State Management Using GetX.pdf
Flutter State Management Using GetX.pdfFlutter State Management Using GetX.pdf
Flutter State Management Using GetX.pdfKaty Slemon
 
google play service 7.8 & new tech in M
google play service 7.8 & new tech in M google play service 7.8 & new tech in M
google play service 7.8 & new tech in M Ted Liang
 

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

Applications
ApplicationsApplications
Applications
 
Creating an Uber Clone - Part IX.pdf
Creating an Uber Clone - Part IX.pdfCreating an Uber Clone - Part IX.pdf
Creating an Uber Clone - Part IX.pdf
 
Android Event and IntentAndroid Event and Intent
Android Event and IntentAndroid Event and IntentAndroid Event and IntentAndroid Event and Intent
Android Event and IntentAndroid Event and Intent
 
Value isnt changing and I cant seem to get the conversion to wor.pdf
Value isnt changing and I cant seem to get the conversion to wor.pdfValue isnt changing and I cant seem to get the conversion to wor.pdf
Value isnt changing and I cant seem to get the conversion to wor.pdf
 
This is a C# project . I am expected to create as this image shows. .pdf
This is a C# project . I am expected to create as this image shows. .pdfThis is a C# project . I am expected to create as this image shows. .pdf
This is a C# project . I am expected to create as this image shows. .pdf
 
Eclipse Tricks
Eclipse TricksEclipse Tricks
Eclipse Tricks
 
Java awt
Java awtJava awt
Java awt
 
Advanced Silverlight
Advanced SilverlightAdvanced Silverlight
Advanced Silverlight
 
Qt Workshop
Qt WorkshopQt Workshop
Qt Workshop
 
What's New in Android
What's New in AndroidWhat's New in Android
What's New in Android
 
20180721 code defragment
20180721 code defragment20180721 code defragment
20180721 code defragment
 
Advancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and GesturesAdvancing the UI — Part 1: Look, Motion, and Gestures
Advancing the UI — Part 1: Look, Motion, and Gestures
 
Swing
SwingSwing
Swing
 
mobl
moblmobl
mobl
 
Adapting to Tablets and Desktops - Part 1.pdf
Adapting to Tablets and Desktops - Part 1.pdfAdapting to Tablets and Desktops - Part 1.pdf
Adapting to Tablets and Desktops - Part 1.pdf
 
Adopting 3D Touch in your apps
Adopting 3D Touch in your appsAdopting 3D Touch in your apps
Adopting 3D Touch in your apps
 
F# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, HerefordF# Presentation for SmartDevs, Hereford
F# Presentation for SmartDevs, Hereford
 
Creating custom views
Creating custom viewsCreating custom views
Creating custom views
 
Flutter State Management Using GetX.pdf
Flutter State Management Using GetX.pdfFlutter State Management Using GetX.pdf
Flutter State Management Using GetX.pdf
 
google play service 7.8 & new tech in M
google play service 7.8 & new tech in M google play service 7.8 & new tech in M
google play service 7.8 & new tech in M
 

More from ShaiAlmog1

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

More from ShaiAlmog1 (20)

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

Recently uploaded

Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 

Recently uploaded (20)

Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 

Creating an Uber Clone - Part XIX - Transcript.pdf

  • 1. Creating an Uber Clone - Part XIX Now that the basic "infrastructure" is out of the way we'll start wiring it into the UI starting with search. The initial search UI I did in the mockup was very "naive". It just toggled the completion on and off.
  • 2. Search UI ✦Swipe ✦Completion ✦Map location picking © Codename One 2017 all rights reserved The real Uber app allows us to swipe the search UI down and then pick a location in the map using a pin. This then updates the text field with the selected location. Alternatively, if you type into the text field locations are suggested to you as you type. There is a great deal of nuanced behavior in this UI so I only focused on the "big ticket" features. The first big feature is the swipe down feature which allows us to swipe down the completion UI as we type. The second is the actual completion UI where elements update as we type. And finally the ability to point at a location on the Map with a pin and select that location. I didn't implement a lot of the relatively easy features such as bookmarked locations or history in the search UI. Those should be trivial to fill in. In order to implement these I moved most of the UI logic into separate classes specifically AutoCompleteAddressInput & CompletionContainer both of which I’ll discuss shortly
  • 3. Search Completion © Codename One 2017 all rights reserved A major feature of AutoCompleteAddressInput is its ability to fold/unfold the CompletionContainer. It accomplishes this by binding pointer listeners to the parent form and using them to implement the drag and drop behavior. On the left you can see the search UI with completion suggestions appearing below.
 When the suggestions are dragged down we can pick the location from the map as you can see on the right. As the map is dragged the location name is updated into the text field
  • 4. public class AutoCompleteAddressInput extends TextField { private final Container layers; private int firstX = -1, firstY = -1; private boolean dragStarted; private CompletionContainer completion; private ActionListener<ActionEvent> dragListener, releaseListener; private Location currentLocation; private boolean blockChangeEvent; public AutoCompleteAddressInput(String value, String hint, Container layers, CompletionContainer completion) { super(value, hint, 40, TextField.ANY); this.completion = completion; this.layers = layers; getHintLabel().setUIID("FromToTextFieldHint"); setUIID("FromToTextField"); addDataChangedListener((i, ii) -> { if(blockChangeEvent) { return; } if(!getText().equals(value)) { completion.updateCompletion(getText(), this); } AutoCompleteAddressInput I refactored some of the code from the MapForm class into the AutoCompleteAddressInput class. It made it easier to implement some of the related logic. I chose to derive TextField rather than encapsulate it mostly due to convenience. Encapsulation would have worked just as well for this case
  • 5. public class AutoCompleteAddressInput extends TextField { private final Container layers; private int firstX = -1, firstY = -1; private boolean dragStarted; private CompletionContainer completion; private ActionListener<ActionEvent> dragListener, releaseListener; private Location currentLocation; private boolean blockChangeEvent; public AutoCompleteAddressInput(String value, String hint, Container layers, CompletionContainer completion) { super(value, hint, 40, TextField.ANY); this.completion = completion; this.layers = layers; getHintLabel().setUIID("FromToTextFieldHint"); setUIID("FromToTextField"); addDataChangedListener((i, ii) -> { if(blockChangeEvent) { return; } if(!getText().equals(value)) { completion.updateCompletion(getText(), this); } AutoCompleteAddressInput With the exception of these last two variables every other variable here is in the service of the drag and drop logic
  • 6. public class AutoCompleteAddressInput extends TextField { private final Container layers; private int firstX = -1, firstY = -1; private boolean dragStarted; private CompletionContainer completion; private ActionListener<ActionEvent> dragListener, releaseListener; private Location currentLocation; private boolean blockChangeEvent; public AutoCompleteAddressInput(String value, String hint, Container layers, CompletionContainer completion) { super(value, hint, 40, TextField.ANY); this.completion = completion; this.layers = layers; getHintLabel().setUIID("FromToTextFieldHint"); setUIID("FromToTextField"); addDataChangedListener((i, ii) -> { if(blockChangeEvent) { return; } if(!getText().equals(value)) { completion.updateCompletion(getText(), this); } AutoCompleteAddressInput We use the DataChangedListener to send events to the completion logic, however this callback can be very verbose and it's sometimes invoked by setText. The solution is a special version of `setText` that blocks this callback and reduces the noise in the completion code with the blockChangeEvent variable
  • 7. addDataChangedListener((i, ii) -> { if(blockChangeEvent) { return; } if(!getText().equals(value)) { completion.updateCompletion(getText(), this); } }); } public void setTextNoEvent(String text) { blockChangeEvent = true; setText(text); blockChangeEvent = false; } @Override protected void focusGained() { completion.initCompletionBar(); } @Override protected void deinitialize() { AutoCompleteAddressInput The last focused text field is the one that now handles the completion so if the user was in the to text field everything typed will now impact the completion for to and visa versa
  • 8. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput Pointer listeners on the Form allow us to detect pointer events everywhere. We bind them in the initComponent method and remove them in the deinitialize method. This prevents a memory leak and a situation where pointer processing code keeps running and taking up CPU. deinitialize is invoked when a component is removed from the UI or its hierarchy is removed. It's also invoked when a different Form is shown instead of the current Form
  • 9. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput initComponent is invoked when a component is "there". It will be invoked if a component is added to an already showing Form or if a parent Form is shown. You can rely on initComponent and deinitialize working in tandem. They might be invoked multiple times in valid situations for instance a Dialog shown on top of a Form triggers a deinitialize on the components of the Form followed by an initComponent when it's disposed
  • 10. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput Despite using the shorthand lambda syntax for event handling I need to keep a reference to the drag and release event objects so I can remove them later
  • 11. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput The dragged element is always the second element (0 is the first). It can be dragged between the CENTER location and the SOUTH location
  • 12. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput If this is indeed a drag operation we'd like to block the event from propagating onwards
  • 13. protected void deinitialize() { if(dragListener != null) { Form f = getComponentForm(); f.removePointerDraggedListener(dragListener); f.removePointerReleasedListener(dragListener); } super.deinitialize(); } @Override protected void initComponent() { super.initComponent(); if(dragListener == null) { dragListener = e -> { Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); if(dragStarted) { e.consume(); cmp.getUnselectedStyle().setMarginUnit(Style.UNIT_TYPE_PIXELS); if(dragUp) { cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); AutoCompleteAddressInput When a component is in the SOUTH we set its preferred size to one 8th of the display height so it won't peek up too much. When its dragged up we just increase that size during drag
  • 14. cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); } else { cmp.getUnselectedStyle().setMarginTop( Math.max(0, e.getY() - firstY)); } layers.revalidate(); } else { Component draggedCmp = getComponentForm().getComponentAt(e.getX(), e.getY()); if(!draggedCmp.isChildOf((Container)cmp)) { return; } if(firstX == -1) { firstX = e.getX(); firstY = e.getY(); } if((!dragUp && e.getY() - firstY > convertToPixels(2)) || (dragUp && firstY - e.getY() > convertToPixels(2))) { e.consume(); dragStarted = true; } AutoCompleteAddressInput Components in the CENTER ignore their preferred size and take up available space so we use margin to provide the drag effect
  • 15. cmp.setPreferredSize(new Dimension(getDisplayWidth(), firstY - e.getY() + getDisplayHeight() / 8)); } else { cmp.getUnselectedStyle().setMarginTop( Math.max(0, e.getY() - firstY)); } layers.revalidate(); } else { Component draggedCmp = getComponentForm().getComponentAt(e.getX(), e.getY()); if(!draggedCmp.isChildOf((Container)cmp)) { return; } if(firstX == -1) { firstX = e.getX(); firstY = e.getY(); } if((!dragUp && e.getY() - firstY > convertToPixels(2)) || (dragUp && firstY - e.getY() > convertToPixels(2))) { e.consume(); dragStarted = true; } AutoCompleteAddressInput This prevents a drag event on a different region in the form from triggering this event. E.g. if a user drags the map
  • 16. (dragUp && firstY - e.getY() > convertToPixels(2))) { e.consume(); dragStarted = true; } } }; getComponentForm().addPointerDraggedListener(dragListener); releaseListener = e -> { if(dragStarted) { e.consume(); Component cmp = layers.getComponentAt(1); boolean dragUp = layers.getLayout(). getComponentConstraint(cmp).equals(SOUTH); cmp.remove(); cmp.setUIID(cmp.getUIID()); boolean animateDown; if(dragUp) { animateDown = !(firstY - e.getY() > convertToPixels(8)); } else { animateDown = e.getY() - firstY > convertToPixels(8); } if(animateDown) { layers.add(SOUTH, cmp); AutoCompleteAddressInput Dragging just displayed a motion. We now need to remove the component and place it where it should be. We also reset the UIID so styling changes (e.g. margin, unit type etc.) will reset to the default
  • 17. if(dragUp) { animateDown = !(firstY - e.getY() > convertToPixels(8)); } else { animateDown = e.getY() - firstY > convertToPixels(8); } if(animateDown) { layers.add(SOUTH, cmp); cmp.setPreferredSize( new Dimension(getDisplayWidth(), getDisplayHeight() / 8)); Style s = cmp.getUnselectedStyle(); s.setMarginUnit(Style.UNIT_TYPE_DIPS); s.setMarginLeft(3); s.setMarginRight(3); } else { layers.add(CENTER, cmp); cmp.setPreferredSize(null); } layers.animateLayout(200); firstX = -1; firstY = -1; dragStarted = false; } }; AutoCompleteAddressInput When we place the container in the SOUTH we set the preferred size and margin to match. When we place it in the CENTER we set the preferred size to null which is a special case that resets previous manual settings and restores the default
  • 18. s.setMarginRight(3); } else { layers.add(CENTER, cmp); cmp.setPreferredSize(null); } layers.animateLayout(200); firstX = -1; firstY = -1; dragStarted = false; } }; getComponentForm().addPointerReleasedListener(releaseListener); } } public Location getCurrentLocation() { return currentLocation; } public void setCurrentLocation(Location currentLocation) { this.currentLocation = currentLocation; } } AutoCompleteAddressInput The location of a text field uses strings but what we really care about is coordinates on the map which is why I store them here. This is used both by the map pin logic and by the search logic we will use later on