SlideShare a Scribd company logo
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 language
Mark 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
 
Web api's
Web api'sWeb api's
Web api's
umesh patil
 
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.pdf
ShaiAlmog1
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy codeShriKant Vashishtha
 
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
Getting value from IoT, Integration and Data Analytics
 
Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8Pragmatic functional refactoring with java 8
Pragmatic functional refactoring with java 8
RichardWarburton
 
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
 
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
 
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
Oluwaleke 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.pptx
JohnRehldeGracia
 
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
ShaiAlmog1
 
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
aragozin
 
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 answers
Quratulain Naqvi
 
Practices For Becoming A Better Programmer
Practices For Becoming A Better ProgrammerPractices For Becoming A Better Programmer
Practices For Becoming A Better Programmer
Srikanth Shreenivas
 
Android training day 5
Android training day 5Android training day 5
Android training day 5
Vivek 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 modeling
Codemotion
 

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.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 
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
ShaiAlmog1
 

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

Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
Dorra BARTAGUIZ
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
Ana-Maria Mihalceanu
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
Jemma Hussein Allen
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Product School
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
UiPathCommunity
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 

Recently uploaded (20)

Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Elevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object CalisthenicsElevating Tactical DDD Patterns Through Object Calisthenics
Elevating Tactical DDD Patterns Through Object Calisthenics
 
Monitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR EventsMonitoring Java Application Security with JDK Tools and JFR Events
Monitoring Java Application Security with JDK Tools and JFR Events
 
Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
The Future of Platform Engineering
The Future of Platform EngineeringThe Future of Platform Engineering
The Future of Platform Engineering
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...Designing Great Products: The Power of Design and Leadership by Chief Designe...
Designing Great Products: The Power of Design and Leadership by Chief Designe...
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder – active learning and UiPath LLMs for do...
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 

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.