SlideShare a Scribd company logo
1 of 29
Download to read offline
Creating a Facebook Clone - Part XXXIII
We are finally ready to tie this into the UI of the client
© Codename One 2017 all rights reserved
The search UI is in a dedicated form. It works as you type and lets you toggle between people/post search modes by pressing the buttons on the top right corner.
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
Lets dive into the code starting with the fields and general types…

This is set to true when we are in people search mode
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
We use this flag to detect if the search text actually changed
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
This timer waits to make sure the user isn't still typing, so we won't send too many search queries at once
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
The time of the last search helps us decide how long we should wait for the upcoming search query so we don't send too many queries
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
This is the search text field where the user can type
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
Search results add up into the InfiniteContainer for paging/refresh capabilities
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
If search text is less than 2 characters we shouldn't start searching
public class SearchForm extends Form {
private boolean searchPeople;
private String lastSearchValue;
private UITimer pendingTimer;
private long lastSearchTime;
private TextField searchField = new TextField();
private InfiniteContainer ic = new InfiniteContainer() {
@Override
public Component[] fetchComponents(int index, int amount) {
if(searchField.getText().length() < 2) {
return null;
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
SearchForm
This is the standard page calculation logic that we have in other InfiniteContainer classes
}
int page = index / amount;
if(index % amount > 0) {
page++;
}
List<Component> response = new ArrayList<>();
if(searchPeople) {
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
}
for(User u : results) {
response.add(createEntry(u));
}
} else {
List<Post> results = ServerAPI.searchPosts(
searchField.getText(), page, amount);
if(results == null) {
return null;
}
for(Post u : results) {
response.add(createEntry(u));
}
}
SearchForm
We build results for people or posts in the same way, there are 2 createEntry methods that cover the logic for both cases
List<User> results = ServerAPI.searchPeople(
searchField.getText(), page, amount);
if(results == null) {
return null;
}
for(User u : results) {
response.add(createEntry(u));
}
} else {
List<Post> results = ServerAPI.searchPosts(
searchField.getText(), page, amount);
if(results == null) {
return null;
}
for(Post u : results) {
response.add(createEntry(u));
}
}
if(response.isEmpty()) {
return null;
}
return UIUtils.toArray(response);
}
};
SearchForm
The resulting components are returned on null in the case of an empty set. This is pretty direct and should be relatively obvious, the two big pieces that are obviously
missing from this code are the createEntry methods so lets get to them…
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
}
}
private Component createEntry(User u) {
MultiButton mb = new MultiButton(u.fullName());
mb.setIcon(u.getAvatar(8));
mb.addActionListener(e -> new UserForm(u).show());
return mb;
}
private Component createEntry(Post p) {
MultiButton mb = new MultiButton(p.title.get());
mb.setTextLine2(p.content.get());
mb.setIcon(p.user.get().getAvatar(8));
mb.addActionListener(e -> new PostForm(p).show());
return mb;
}
}
SearchForm
The version that accepts the User object just creates a MultiButton for that. The same is true for the post entry
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
}
}
private Component createEntry(User u) {
MultiButton mb = new MultiButton(u.fullName());
mb.setIcon(u.getAvatar(8));
mb.addActionListener(e -> new UserForm(u).show());
return mb;
}
private Component createEntry(Post p) {
MultiButton mb = new MultiButton(p.title.get());
mb.setTextLine2(p.content.get());
mb.setIcon(p.user.get().getAvatar(8));
mb.addActionListener(e -> new PostForm(p).show());
return mb;
}
}
SearchForm
I’ll get to the event handling code later, the UserForm & PostForm mentioned here don’t exist yet so I’ll discuss them after finishing the search class.

This is pretty trivial the next step isn't as simple…
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
We use BorderLayout so the InfiniteContainer can fit in the center, there is no other reason
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
The Title UIID makes the search field fit into the title area
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
In iOS the title is centered by default and that doesn't look good while editing so we left align explicitly for this case
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
This is the search operation, with every change to the text field we call the updateSearch method which performs the actual search
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
setTitleComponent places the component in the title area instead of the label that's there by default
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
This closes the search form by going to the previous form
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
These are the two buttons in the title area that let us toggle people & post search modes. We invoke refresh when there is a change which triggers a call to
fetchComponents in the InfiniteContainer for the first page
return UIUtils.toArray(response);
}
};
public SearchForm() {
super(new BorderLayout());
searchField.setUIID("Title");
searchField.getAllStyles().setAlignment(LEFT);
searchField.addDataChangedListener((i, ii) -> updateSearch());
Toolbar tb = getToolbar();
tb.setTitleComponent(searchField);
Form previous = getCurrentForm();
tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e ->
previous.showBack());
tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> {
searchPeople = true;
ic.refresh();
});
tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> {
searchPeople = false;
ic.refresh();
});
add(CENTER, ic);
setEditOnShow(searchField);
}
SearchForm
When we show the form the search field will instantly launch into editing mode, this is important with a virtual keyboard so it won't just have focus it will actually be in edit
mode
add(CENTER, ic);
setEditOnShow(searchField);
}
private void updateSearch() {
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
SearchForm
This leads us to the updateSearch() method which performs the actual search logic
add(CENTER, ic);
setEditOnShow(searchField);
}
private void updateSearch() {
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
SearchForm
If the text didn't change we don't do anything
add(CENTER, ic);
setEditOnShow(searchField);
}
private void updateSearch() {
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
SearchForm
If we have a search pending we kill that search request, since this all runs on the EDT this is a search that didn't start yet
add(CENTER, ic);
setEditOnShow(searchField);
}
private void updateSearch() {
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
SearchForm
If we already started a search we make sure it wasn't too soon, otherwise we'll postpone this search for later
add(CENTER, ic);
setEditOnShow(searchField);
}
private void updateSearch() {
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
SearchForm
When the timer elapses we update the search time and refresh, notice that since this is a UITimer the elapsing happens on the EDT
String text = searchField.getText();
if(text.length() > 2) {
if(lastSearchValue != null) {
if(lastSearchValue.equalsIgnoreCase(text)) {
return;
}
if(pendingTimer != null) {
pendingTimer.cancel();
}
long t = System.currentTimeMillis();
if(t - lastSearchTime < 800) {
pendingTimer = UITimer.timer((int)(t - lastSearchTime),
false, this, () -> {
lastSearchTime = System.currentTimeMillis();
ic.refresh();
});
return;
}
}
lastSearchTime = System.currentTimeMillis();
ic.refresh();
}
}
private Component createEntry(User u) {
MultiButton mb = new MultiButton(u.fullName());
SearchForm
If there is nothing pending we just do the search right now and refresh the InfiniteContainer. With this the search form should work and show search results instantly!
mainUI.addTab("", MATERIAL_WEB, 5f, new NewsfeedContainer());
FloatingActionButton fab =
FloatingActionButton.createFAB(MATERIAL_IMPORT_CONTACTS);
Container friends = fab.bindFabToContainer(new FriendsContainer());
fab.addActionListener(e -> uploadContacts());
mainUI.addTab("", MATERIAL_PEOPLE_OUTLINE, 5f,
friends);
mainUI.addTab("", MATERIAL_NOTIFICATIONS_NONE,
5f, new NotificationsContainer());
mainUI.addTab("", MATERIAL_MENU, 5f,
new MoreContainer());
add(CENTER, mainUI);
getToolbar().addMaterialCommandToLeftBar("",
MATERIAL_CAMERA_ALT, 4, e -> {});
getToolbar().addMaterialCommandToRightBar("",
MATERIAL_CHAT, 4, e -> {});
Button searchButton = new Button("Search", "TitleSearch");
setMaterialIcon(searchButton, MATERIAL_SEARCH);
getToolbar().setTitleComponent(searchButton);
searchButton.addActionListener(e -> new SearchForm().show());
}
private void uploadContacts() {
MainForm
The one last piece of the puzzle is plugging it into the UI which is actually trivial... We add this line to the MainForm constructor. With that you can now click the search
button and it will actually work with the data from the database.

More Related Content

Similar to Creating a Facebook Clone - Part XXXIII - Transcript.pdf

Mixing functional programming approaches in an object oriented language
Mixing functional programming approaches in an object oriented languageMixing functional programming approaches in an object oriented language
Mixing functional programming approaches in an object oriented languageMark Needham
 
Cis407 a ilab 4 web application development devry university
Cis407 a ilab 4 web application development devry universityCis407 a ilab 4 web application development devry university
Cis407 a ilab 4 web application development devry universitylhkslkdh89009
 
C# Starter L04-Collections
C# Starter L04-CollectionsC# Starter L04-Collections
C# Starter L04-CollectionsMohammad Shaker
 
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfCreating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfShaiAlmog1
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy codeShriKant Vashishtha
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8RichardWarburton
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mark Needham
 
Mixing Functional and Object Oriented Approaches to Programming in C#
Mixing Functional and Object Oriented Approaches to Programming in C#Mixing Functional and Object Oriented Approaches to Programming in C#
Mixing Functional and Object Oriented Approaches to Programming in C#Skills Matter
 
All you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsAll you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsOluwaleke Fakorede
 
Computer-programming-User-defined-function-1.pptx
Computer-programming-User-defined-function-1.pptxComputer-programming-User-defined-function-1.pptx
Computer-programming-User-defined-function-1.pptxJohnRehldeGracia
 
Creating an Uber Clone - Part XX - Transcript.pdf
Creating an Uber Clone - Part XX - Transcript.pdfCreating an Uber Clone - Part XX - Transcript.pdf
Creating an Uber Clone - Part XX - Transcript.pdfShaiAlmog1
 
Coherence SIG: Advanced usage of indexes in coherence
Coherence SIG: Advanced usage of indexes in coherenceCoherence SIG: Advanced usage of indexes in coherence
Coherence SIG: Advanced usage of indexes in coherencearagozin
 
How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF Luc Bors
 
OOP program questions with answers
OOP program questions with answersOOP program questions with answers
OOP program questions with answersQuratulain Naqvi
 
Practices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerPractices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerSrikanth Shreenivas
 
Android training day 5
Android training day 5Android training day 5
Android training day 5Vivek Bhusal
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingCodemotion
 

Similar to Creating a Facebook Clone - Part XXXIII - Transcript.pdf (20)

Mixing functional programming approaches in an object oriented language
Mixing functional programming approaches in an object oriented languageMixing functional programming approaches in an object oriented language
Mixing functional programming approaches in an object oriented language
 
Cis407 a ilab 4 web application development devry university
Cis407 a ilab 4 web application development devry universityCis407 a ilab 4 web application development devry university
Cis407 a ilab 4 web application development devry university
 
Web api's
Web api'sWeb api's
Web api's
 
C# Starter L04-Collections
C# Starter L04-CollectionsC# Starter L04-Collections
C# Starter L04-Collections
 
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfCreating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
How te bring common UI patterns to ADF
How te bring common UI patterns to ADFHow te bring common UI patterns to ADF
How te bring common UI patterns to ADF
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
 
Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#Mixing functional and object oriented approaches to programming in C#
Mixing functional and object oriented approaches to programming in C#
 
Mixing Functional and Object Oriented Approaches to Programming in C#
Mixing Functional and Object Oriented Approaches to Programming in C#Mixing Functional and Object Oriented Approaches to Programming in C#
Mixing Functional and Object Oriented Approaches to Programming in C#
 
All you need to know about JavaScript Functions
All you need to know about JavaScript FunctionsAll you need to know about JavaScript Functions
All you need to know about JavaScript Functions
 
C# labprograms
C# labprogramsC# labprograms
C# labprograms
 
Computer-programming-User-defined-function-1.pptx
Computer-programming-User-defined-function-1.pptxComputer-programming-User-defined-function-1.pptx
Computer-programming-User-defined-function-1.pptx
 
Creating an Uber Clone - Part XX - Transcript.pdf
Creating an Uber Clone - Part XX - Transcript.pdfCreating an Uber Clone - Part XX - Transcript.pdf
Creating an Uber Clone - Part XX - Transcript.pdf
 
Coherence SIG: Advanced usage of indexes in coherence
Coherence SIG: Advanced usage of indexes in coherenceCoherence SIG: Advanced usage of indexes in coherence
Coherence SIG: Advanced usage of indexes in coherence
 
How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF How to Bring Common UI Patterns to ADF
How to Bring Common UI Patterns to ADF
 
OOP program questions with answers
OOP program questions with answersOOP program questions with answers
OOP program questions with answers
 
Practices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerPractices For Becoming A Better Programmer
Practices For Becoming A Better Programmer
 
Android training day 5
Android training day 5Android training day 5
Android training day 5
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 

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

Quantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation ComputingQuantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation ComputingWSO2
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Victor Rentea
 
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...WSO2
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FMESafe Software
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodJuan lago vázquez
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Bhuvaneswari Subramani
 
API Governance and Monetization - The evolution of API governance
API Governance and Monetization -  The evolution of API governanceAPI Governance and Monetization -  The evolution of API governance
API Governance and Monetization - The evolution of API governanceWSO2
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Orbitshub
 
ChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps ProductivityChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps ProductivityVictorSzoltysek
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Victor Rentea
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingEdi Saputra
 
Navigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseNavigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseWSO2
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontologyjohnbeverley2021
 
Simplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxSimplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxMarkSteadman7
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityWSO2
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightSafe Software
 

Recently uploaded (20)

Quantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation ComputingQuantum Leap in Next-Generation Computing
Quantum Leap in Next-Generation Computing
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
WSO2 Micro Integrator for Enterprise Integration in a Decentralized, Microser...
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
API Governance and Monetization - The evolution of API governance
API Governance and Monetization -  The evolution of API governanceAPI Governance and Monetization -  The evolution of API governance
API Governance and Monetization - The evolution of API governance
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
ChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps ProductivityChatGPT and Beyond - Elevating DevOps Productivity
ChatGPT and Beyond - Elevating DevOps Productivity
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
Navigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseNavigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern Enterprise
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Simplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxSimplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptx
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and Insight
 

Creating a Facebook Clone - Part XXXIII - Transcript.pdf

  • 1. Creating a Facebook Clone - Part XXXIII We are finally ready to tie this into the UI of the client
  • 2. © Codename One 2017 all rights reserved The search UI is in a dedicated form. It works as you type and lets you toggle between people/post search modes by pressing the buttons on the top right corner.
  • 3. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm Lets dive into the code starting with the fields and general types… This is set to true when we are in people search mode
  • 4. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm We use this flag to detect if the search text actually changed
  • 5. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm This timer waits to make sure the user isn't still typing, so we won't send too many search queries at once
  • 6. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm The time of the last search helps us decide how long we should wait for the upcoming search query so we don't send too many queries
  • 7. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm This is the search text field where the user can type
  • 8. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm Search results add up into the InfiniteContainer for paging/refresh capabilities
  • 9. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm If search text is less than 2 characters we shouldn't start searching
  • 10. public class SearchForm extends Form { private boolean searchPeople; private String lastSearchValue; private UITimer pendingTimer; private long lastSearchTime; private TextField searchField = new TextField(); private InfiniteContainer ic = new InfiniteContainer() { @Override public Component[] fetchComponents(int index, int amount) { if(searchField.getText().length() < 2) { return null; } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; SearchForm This is the standard page calculation logic that we have in other InfiniteContainer classes
  • 11. } int page = index / amount; if(index % amount > 0) { page++; } List<Component> response = new ArrayList<>(); if(searchPeople) { List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; } for(User u : results) { response.add(createEntry(u)); } } else { List<Post> results = ServerAPI.searchPosts( searchField.getText(), page, amount); if(results == null) { return null; } for(Post u : results) { response.add(createEntry(u)); } } SearchForm We build results for people or posts in the same way, there are 2 createEntry methods that cover the logic for both cases
  • 12. List<User> results = ServerAPI.searchPeople( searchField.getText(), page, amount); if(results == null) { return null; } for(User u : results) { response.add(createEntry(u)); } } else { List<Post> results = ServerAPI.searchPosts( searchField.getText(), page, amount); if(results == null) { return null; } for(Post u : results) { response.add(createEntry(u)); } } if(response.isEmpty()) { return null; } return UIUtils.toArray(response); } }; SearchForm The resulting components are returned on null in the case of an empty set. This is pretty direct and should be relatively obvious, the two big pieces that are obviously missing from this code are the createEntry methods so lets get to them…
  • 13. }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); } } private Component createEntry(User u) { MultiButton mb = new MultiButton(u.fullName()); mb.setIcon(u.getAvatar(8)); mb.addActionListener(e -> new UserForm(u).show()); return mb; } private Component createEntry(Post p) { MultiButton mb = new MultiButton(p.title.get()); mb.setTextLine2(p.content.get()); mb.setIcon(p.user.get().getAvatar(8)); mb.addActionListener(e -> new PostForm(p).show()); return mb; } } SearchForm The version that accepts the User object just creates a MultiButton for that. The same is true for the post entry
  • 14. }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); } } private Component createEntry(User u) { MultiButton mb = new MultiButton(u.fullName()); mb.setIcon(u.getAvatar(8)); mb.addActionListener(e -> new UserForm(u).show()); return mb; } private Component createEntry(Post p) { MultiButton mb = new MultiButton(p.title.get()); mb.setTextLine2(p.content.get()); mb.setIcon(p.user.get().getAvatar(8)); mb.addActionListener(e -> new PostForm(p).show()); return mb; } } SearchForm I’ll get to the event handling code later, the UserForm & PostForm mentioned here don’t exist yet so I’ll discuss them after finishing the search class. This is pretty trivial the next step isn't as simple…
  • 15. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm We use BorderLayout so the InfiniteContainer can fit in the center, there is no other reason
  • 16. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm The Title UIID makes the search field fit into the title area
  • 17. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm In iOS the title is centered by default and that doesn't look good while editing so we left align explicitly for this case
  • 18. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm This is the search operation, with every change to the text field we call the updateSearch method which performs the actual search
  • 19. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm setTitleComponent places the component in the title area instead of the label that's there by default
  • 20. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm This closes the search form by going to the previous form
  • 21. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm These are the two buttons in the title area that let us toggle people & post search modes. We invoke refresh when there is a change which triggers a call to fetchComponents in the InfiniteContainer for the first page
  • 22. return UIUtils.toArray(response); } }; public SearchForm() { super(new BorderLayout()); searchField.setUIID("Title"); searchField.getAllStyles().setAlignment(LEFT); searchField.addDataChangedListener((i, ii) -> updateSearch()); Toolbar tb = getToolbar(); tb.setTitleComponent(searchField); Form previous = getCurrentForm(); tb.addMaterialCommandToLeftBar("", MATERIAL_CLOSE, e -> previous.showBack()); tb.addMaterialCommandToRightBar("", MATERIAL_PERSON, e -> { searchPeople = true; ic.refresh(); }); tb.addMaterialCommandToRightBar("", MATERIAL_PAGES, e -> { searchPeople = false; ic.refresh(); }); add(CENTER, ic); setEditOnShow(searchField); } SearchForm When we show the form the search field will instantly launch into editing mode, this is important with a virtual keyboard so it won't just have focus it will actually be in edit mode
  • 23. add(CENTER, ic); setEditOnShow(searchField); } private void updateSearch() { String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); SearchForm This leads us to the updateSearch() method which performs the actual search logic
  • 24. add(CENTER, ic); setEditOnShow(searchField); } private void updateSearch() { String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); SearchForm If the text didn't change we don't do anything
  • 25. add(CENTER, ic); setEditOnShow(searchField); } private void updateSearch() { String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); SearchForm If we have a search pending we kill that search request, since this all runs on the EDT this is a search that didn't start yet
  • 26. add(CENTER, ic); setEditOnShow(searchField); } private void updateSearch() { String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); SearchForm If we already started a search we make sure it wasn't too soon, otherwise we'll postpone this search for later
  • 27. add(CENTER, ic); setEditOnShow(searchField); } private void updateSearch() { String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); SearchForm When the timer elapses we update the search time and refresh, notice that since this is a UITimer the elapsing happens on the EDT
  • 28. String text = searchField.getText(); if(text.length() > 2) { if(lastSearchValue != null) { if(lastSearchValue.equalsIgnoreCase(text)) { return; } if(pendingTimer != null) { pendingTimer.cancel(); } long t = System.currentTimeMillis(); if(t - lastSearchTime < 800) { pendingTimer = UITimer.timer((int)(t - lastSearchTime), false, this, () -> { lastSearchTime = System.currentTimeMillis(); ic.refresh(); }); return; } } lastSearchTime = System.currentTimeMillis(); ic.refresh(); } } private Component createEntry(User u) { MultiButton mb = new MultiButton(u.fullName()); SearchForm If there is nothing pending we just do the search right now and refresh the InfiniteContainer. With this the search form should work and show search results instantly!
  • 29. mainUI.addTab("", MATERIAL_WEB, 5f, new NewsfeedContainer()); FloatingActionButton fab = FloatingActionButton.createFAB(MATERIAL_IMPORT_CONTACTS); Container friends = fab.bindFabToContainer(new FriendsContainer()); fab.addActionListener(e -> uploadContacts()); mainUI.addTab("", MATERIAL_PEOPLE_OUTLINE, 5f, friends); mainUI.addTab("", MATERIAL_NOTIFICATIONS_NONE, 5f, new NotificationsContainer()); mainUI.addTab("", MATERIAL_MENU, 5f, new MoreContainer()); add(CENTER, mainUI); getToolbar().addMaterialCommandToLeftBar("", MATERIAL_CAMERA_ALT, 4, e -> {}); getToolbar().addMaterialCommandToRightBar("", MATERIAL_CHAT, 4, e -> {}); Button searchButton = new Button("Search", "TitleSearch"); setMaterialIcon(searchButton, MATERIAL_SEARCH); getToolbar().setTitleComponent(searchButton); searchButton.addActionListener(e -> new SearchForm().show()); } private void uploadContacts() { MainForm The one last piece of the puzzle is plugging it into the UI which is actually trivial... We add this line to the MainForm constructor. With that you can now click the search button and it will actually work with the data from the database.