SlideShare a Scribd company logo
1 of 28
Download to read offline
Creating a Facebook Clone - Part V
We need to make a decision on the UI design: Should we use the iOS design, the Android design or both?

Android & iOS differ greatly in the login process for Facebook. I'm assuming the reason for this is Apples review process that places restrictions on what Facebook can
do.
© Codename One 2017 all rights reserved
I think starting with terms & conditions makes more sense. You get that out of the way and then proceed directly to user information.

Other than that for the most part I chose to go with a design that looks more like the Android design for the signup wizard. Before I go into the code lets look at the first
Form in the signup wizard so we understand what we are up against.

We need to adapt the title area to landscape mode.

We need to have different back command behavior for iOS.

We need a rich text component to provide multiline text with links

The first two are common to all the forms. To facilitate that I decided to create a generic Form that will handle all the stages of the signup: SignupForm. I'll discuss that
shortly. But first lets discuss the rich text support.
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
A very common request is support for rich text in Codename One, this is hard to do in a generic/performant cross platform way but can be done easily for simple tasks
like this.

Notice that for a lot of use cases I would just use BrowserComponent. Here that won't be ideal because I want deep integration with the UI and deep control.

Since we already have an XML parser that's perfectly capable of parsing HTML I decided to use a very simple HTML based syntax. I also added support for bold & italic
while demonstrating that simple things like line breaks still work. 

Lets go over the code step by step.

This is a Container, as we parse we add elements into it
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
This is the HTML text we set, it's useful for the getText() method
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
Font size is hardcoded in millimeters for simplicity
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
The EventDispatcher lets us broadcast link click events as ActionEvent
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
The following variables are used by the parser to store temporary parser state as it builds the UI
public class RichTextView extends Container {
private String text;
private final float fontSize = 2.6f;
private EventDispatcher listeners = new EventDispatcher();
private Font currentFont;
private int currentColor = 0;
private String currentLink;
private Style lastCmp;
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
RichTextView
These are the fonts and constants we use when the parser asks us to create a component
private Font defaultFont;
private Font boldFont;
private Font italicFont;
private int sizeOfSpace;
public RichTextView() {
init();
}
public RichTextView(String text) {
init();
setText(text);
}
private void init() {
defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize);
boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
sizeOfSpace = defaultFont.charWidth(' ');
currentFont = defaultFont;
}
public void setAlignment(int align) {
((FlowLayout)getLayout()).setAlign(align);
}
RichTextView
The constructors delegate to the init method which in turn initializes the default fonts.
setText(text);
}
private void init() {
defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize);
boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
sizeOfSpace = defaultFont.charWidth(' ');
currentFont = defaultFont;
}
public void setAlignment(int align) {
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
RichTextView
Notice that the default layout is FlowLayout we rely on that to allow aligning the text to the center/right
setText(text);
}
private void init() {
defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize);
boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
sizeOfSpace = defaultFont.charWidth(' ');
currentFont = defaultFont;
}
public void setAlignment(int align) {
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
RichTextView
This method is invoked by the parser to add a component
setText(text);
}
private void init() {
defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize);
boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
sizeOfSpace = defaultFont.charWidth(' ');
currentFont = defaultFont;
}
public void setAlignment(int align) {
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
RichTextView
If the string has spaces we split it to multiple separate strings so each individual component can break a line in the flow layout
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
RichTextView
We add either a Label or a Button that looks like a Label
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
RichTextView
The parser updates currentLink so if we have a link we are within an <a> tag and need to use a button styled to look like a link
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
RichTextView
currentLinkValue is the content of href we just fire the listener event dispatcher with the href content as the source
((FlowLayout)getLayout()).setAlign(align);
}
private void createComponent(String t) {
if(t.indexOf(' ') > -1) {
for(String s : StringUtil.tokenize(t, ' ')) {
createComponent(s);
}
return;
}
Label l;
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
RichTextView
If this isn't a link it's label
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
s.setMargin(0, 0, 0, 0);
lastCmp = s;
add(l);
}
public final void setText(String text) {
this.text = text;
removeAll();
try {
char[] chrs = ("<body>" + text + "</body>").toCharArray();
new Parser().eventParser(new CharArrayReader(chrs));
RichTextView
We remove the spaces, padding & margin. We then use the width of the a space to re-add a space in the form of padding. This allows line breaks on word boundaries
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
s.setMargin(0, 0, 0, 0);
lastCmp = s;
add(l);
}
public final void setText(String text) {
this.text = text;
removeAll();
try {
char[] chrs = ("<body>" + text + "</body>").toCharArray();
new Parser().eventParser(new CharArrayReader(chrs));
RichTextView
The parser might need to remove a space for some special cases e.g. if we close an </a> tag and place a dot right next to it
if(currentLink != null) {
Button b = new Button(t, "Label");
final String currentLinkValue = currentLink;
b.addActionListener(e -> listeners.fireActionEvent(
new ActionEvent(currentLinkValue)));
l = b;
} else {
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
s.setMargin(0, 0, 0, 0);
lastCmp = s;
add(l);
}
public final void setText(String text) {
this.text = text;
removeAll();
try {
char[] chrs = ("<body>" + text + "</body>").toCharArray();
new Parser().eventParser(new CharArrayReader(chrs));
RichTextView
The setText method is the API that sets the HTML to this component
l = new Label(t);
}
Style s = l.getAllStyles();
s.setFont(currentFont);
s.setFgColor(currentColor);
s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
s.setPadding(0, 0, 0, sizeOfSpace);
s.setMargin(0, 0, 0, 0);
lastCmp = s;
add(l);
}
public final void setText(String text) {
this.text = text;
removeAll();
try {
char[] chrs = ("<body>" + text + "</body>").toCharArray();
new Parser().eventParser(new CharArrayReader(chrs));
} catch(IOException err) {
log(err);
}
}
public String getText() {
return text;
}
RichTextView
We wrap the HTML so it's well formed and parse the text using the Parser inner class
this.text = text;
removeAll();
try {
char[] chrs = ("<body>" + text + "</body>").toCharArray();
new Parser().eventParser(new CharArrayReader(chrs));
} catch(IOException err) {
log(err);
}
}
public String getText() {
return text;
}
public void addLinkListener(ActionListener al) {
listeners.addListener(al);
}
public void removeLinkListener(ActionListener al) {
listeners.removeListener(al);
}
class Parser extends XMLParser {
@Override
protected void textElement(String text) {
RichTextView
These add listeners so when a user clicks on a link these listeners will receive the event from the event dispatcher
listeners.addListener(al);
}
public void removeLinkListener(ActionListener al) {
listeners.removeListener(al);
}
class Parser extends XMLParser {
@Override
protected void textElement(String text) {
if(text.length() > 0) {
if(lastCmp != null && text.startsWith(" ")) {
lastCmp.setPadding(0, 0, 0, sizeOfSpace);
}
createComponent(text);
if(!text.endsWith(" ")) {
lastCmp.setPadding(0, 0, 0, 0);
}
}
}
@Override
protected boolean startTag(String tag) {
switch(tag.toLowerCase()) {
case "a":
currentColor = 0x4267B2;
RichTextView
The next stage is the Parser inner class, it's a bit big . We derive XMLParser & override callback methods to tokenize the string
listeners.addListener(al);
}
public void removeLinkListener(ActionListener al) {
listeners.removeListener(al);
}
class Parser extends XMLParser {
@Override
protected void textElement(String text) {
if(text.length() > 0) {
if(lastCmp != null && text.startsWith(" ")) {
lastCmp.setPadding(0, 0, 0, sizeOfSpace);
}
createComponent(text);
if(!text.endsWith(" ")) {
lastCmp.setPadding(0, 0, 0, 0);
}
}
}
@Override
protected boolean startTag(String tag) {
switch(tag.toLowerCase()) {
case "a":
currentColor = 0x4267B2;
RichTextView
All the methods here are callbacks in a SAX style parser. We also support a DOM style but I chose a callback approach for performance
listeners.addListener(al);
}
public void removeLinkListener(ActionListener al) {
listeners.removeListener(al);
}
class Parser extends XMLParser {
@Override
protected void textElement(String text) {
if(text.length() > 0) {
if(lastCmp != null && text.startsWith(" ")) {
lastCmp.setPadding(0, 0, 0, sizeOfSpace);
}
createComponent(text);
if(!text.endsWith(" ")) {
lastCmp.setPadding(0, 0, 0, 0);
}
}
}
@Override
protected boolean startTag(String tag) {
switch(tag.toLowerCase()) {
case "a":
currentColor = 0x4267B2;
RichTextView
If the block element starts with a space we add a space to the last block element
listeners.addListener(al);
}
public void removeLinkListener(ActionListener al) {
listeners.removeListener(al);
}
class Parser extends XMLParser {
@Override
protected void textElement(String text) {
if(text.length() > 0) {
if(lastCmp != null && text.startsWith(" ")) {
lastCmp.setPadding(0, 0, 0, sizeOfSpace);
}
createComponent(text);
if(!text.endsWith(" ")) {
lastCmp.setPadding(0, 0, 0, 0);
}
}
}
@Override
protected boolean startTag(String tag) {
switch(tag.toLowerCase()) {
case "a":
currentColor = 0x4267B2;
RichTextView
We remove the space from the previous component if the block doesn't end with a space
}
createComponent(text);
if(!text.endsWith(" ")) {
lastCmp.setPadding(0, 0, 0, 0);
}
}
}
@Override
protected boolean startTag(String tag) {
switch(tag.toLowerCase()) {
case "a":
currentColor = 0x4267B2;
break;
case "b":
currentFont = boldFont;
break;
case "i":
currentFont = italicFont;
break;
}
return true;
}
@Override
protected void endTag(String tag) {
RichTextView
Here we handle the individual HTML tags we want to support by changing the current font or color
currentColor = 0x4267B2;
break;
case "b":
currentFont = boldFont;
break;
case "i":
currentFont = italicFont;
break;
}
return true;
}
@Override
protected void endTag(String tag) {
currentColor = 0;
currentLink = null;
currentFont = defaultFont;
}
@Override
protected void attribute(
String tag, String attributeName, String value) {
if(tag.toLowerCase().equals("a") &&
attributeName.toLowerCase().equals("href")) {
currentLink = value;
}
RichTextView
When a tag ends we clear the current font/color and restore the default, we don't handle complexity like tag nesting etc.
}
@Override
protected void endTag(String tag) {
currentColor = 0;
currentLink = null;
currentFont = defaultFont;
}
@Override
protected void attribute(
String tag, String attributeName, String value) {
if(tag.toLowerCase().equals("a") &&
attributeName.toLowerCase().equals("href")) {
currentLink = value;
}
}
@Override
protected void notifyError(int errorId, String tag,
String attribute, String value, String description) {
log("Error during parsing: " + tag);
}
}
}
RichTextView
When we're in an <a> tag we save the value of the href attribute for use in the event handler code. That's it, we now have a poor mans HTML renderer that we can use
for simple blocks of highlighted text. We'll make use of it soon...

More Related Content

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

Microsoft Silverlight
Microsoft SilverlightMicrosoft Silverlight
Microsoft Silverlightguest3a8196
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)Jose Manuel Pereira Garcia
 
Swift on Raspberry Pi
Swift on Raspberry PiSwift on Raspberry Pi
Swift on Raspberry PiSally Shepard
 
PDF made easy with iText 7
PDF made easy with iText 7PDF made easy with iText 7
PDF made easy with iText 7iText Group nv
 
Maximilian Michels – Google Cloud Dataflow on Top of Apache Flink
Maximilian Michels – Google Cloud Dataflow on Top of Apache FlinkMaximilian Michels – Google Cloud Dataflow on Top of Apache Flink
Maximilian Michels – Google Cloud Dataflow on Top of Apache FlinkFlink Forward
 
21 android2 updated
21 android2 updated21 android2 updated
21 android2 updatedGhanaGTUG
 
Introduction to Shiny for building web apps in R
Introduction to Shiny for building web apps in RIntroduction to Shiny for building web apps in R
Introduction to Shiny for building web apps in RPaul Richards
 
Project: Call Center Management
Project: Call Center ManagementProject: Call Center Management
Project: Call Center Managementpritamkumar
 
Ruby on Rails vs ASP.NET MVC
Ruby on Rails vs ASP.NET MVCRuby on Rails vs ASP.NET MVC
Ruby on Rails vs ASP.NET MVCSimone Chiaretta
 
System programmin practical file
System programmin practical fileSystem programmin practical file
System programmin practical fileAnkit Dixit
 
Developing an ASP.NET Web Application
Developing an ASP.NET Web ApplicationDeveloping an ASP.NET Web Application
Developing an ASP.NET Web ApplicationRishi Kothari
 
Twitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkTwitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkHendy Irawan
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Juan Pablo
 
How to develop asp web applications
How to develop asp web applicationsHow to develop asp web applications
How to develop asp web applicationsDeepankar Pathak
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web ToolkitsYiguang Hu
 
Introduction to JSF
Introduction toJSFIntroduction toJSF
Introduction to JSFSoftServe
 

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

Microsoft Silverlight
Microsoft SilverlightMicrosoft Silverlight
Microsoft Silverlight
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Swift on Raspberry Pi
Swift on Raspberry PiSwift on Raspberry Pi
Swift on Raspberry Pi
 
Next .NET and C#
Next .NET and C#Next .NET and C#
Next .NET and C#
 
PDF made easy with iText 7
PDF made easy with iText 7PDF made easy with iText 7
PDF made easy with iText 7
 
Maximilian Michels – Google Cloud Dataflow on Top of Apache Flink
Maximilian Michels – Google Cloud Dataflow on Top of Apache FlinkMaximilian Michels – Google Cloud Dataflow on Top of Apache Flink
Maximilian Michels – Google Cloud Dataflow on Top of Apache Flink
 
21 android2 updated
21 android2 updated21 android2 updated
21 android2 updated
 
Php intro
Php introPhp intro
Php intro
 
Introduction to Shiny for building web apps in R
Introduction to Shiny for building web apps in RIntroduction to Shiny for building web apps in R
Introduction to Shiny for building web apps in R
 
Gwt and Xtend
Gwt and XtendGwt and Xtend
Gwt and Xtend
 
Project: Call Center Management
Project: Call Center ManagementProject: Call Center Management
Project: Call Center Management
 
Ruby on Rails vs ASP.NET MVC
Ruby on Rails vs ASP.NET MVCRuby on Rails vs ASP.NET MVC
Ruby on Rails vs ASP.NET MVC
 
System programmin practical file
System programmin practical fileSystem programmin practical file
System programmin practical file
 
Developing an ASP.NET Web Application
Developing an ASP.NET Web ApplicationDeveloping an ASP.NET Web Application
Developing an ASP.NET Web Application
 
Twitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian NetworkTwitter Author Prediction from Tweets using Bayesian Network
Twitter Author Prediction from Tweets using Bayesian Network
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#
 
How to develop asp web applications
How to develop asp web applicationsHow to develop asp web applications
How to develop asp web applications
 
Quick Fix Sample
Quick Fix SampleQuick Fix Sample
Quick Fix Sample
 
Google Web Toolkits
Google Web ToolkitsGoogle Web Toolkits
Google Web Toolkits
 
Introduction to JSF
Introduction toJSFIntroduction toJSF
Introduction to JSF
 

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

Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfjimielynbastida
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsAndrey Dotsenko
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 

Recently uploaded (20)

Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdf
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 

Creating a Facebook Clone - Part V - Transcript.pdf

  • 1. Creating a Facebook Clone - Part V We need to make a decision on the UI design: Should we use the iOS design, the Android design or both? Android & iOS differ greatly in the login process for Facebook. I'm assuming the reason for this is Apples review process that places restrictions on what Facebook can do.
  • 2. © Codename One 2017 all rights reserved I think starting with terms & conditions makes more sense. You get that out of the way and then proceed directly to user information. Other than that for the most part I chose to go with a design that looks more like the Android design for the signup wizard. Before I go into the code lets look at the first Form in the signup wizard so we understand what we are up against. We need to adapt the title area to landscape mode. We need to have different back command behavior for iOS. We need a rich text component to provide multiline text with links The first two are common to all the forms. To facilitate that I decided to create a generic Form that will handle all the stages of the signup: SignupForm. I'll discuss that shortly. But first lets discuss the rich text support.
  • 3. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView A very common request is support for rich text in Codename One, this is hard to do in a generic/performant cross platform way but can be done easily for simple tasks like this. Notice that for a lot of use cases I would just use BrowserComponent. Here that won't be ideal because I want deep integration with the UI and deep control. Since we already have an XML parser that's perfectly capable of parsing HTML I decided to use a very simple HTML based syntax. I also added support for bold & italic while demonstrating that simple things like line breaks still work. Lets go over the code step by step. This is a Container, as we parse we add elements into it
  • 4. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView This is the HTML text we set, it's useful for the getText() method
  • 5. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView Font size is hardcoded in millimeters for simplicity
  • 6. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView The EventDispatcher lets us broadcast link click events as ActionEvent
  • 7. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView The following variables are used by the parser to store temporary parser state as it builds the UI
  • 8. public class RichTextView extends Container { private String text; private final float fontSize = 2.6f; private EventDispatcher listeners = new EventDispatcher(); private Font currentFont; private int currentColor = 0; private String currentLink; private Style lastCmp; private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } RichTextView These are the fonts and constants we use when the parser asks us to create a component
  • 9. private Font defaultFont; private Font boldFont; private Font italicFont; private int sizeOfSpace; public RichTextView() { init(); } public RichTextView(String text) { init(); setText(text); } private void init() { defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize); boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize); italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize); sizeOfSpace = defaultFont.charWidth(' '); currentFont = defaultFont; } public void setAlignment(int align) { ((FlowLayout)getLayout()).setAlign(align); } RichTextView The constructors delegate to the init method which in turn initializes the default fonts.
  • 10. setText(text); } private void init() { defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize); boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize); italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize); sizeOfSpace = defaultFont.charWidth(' '); currentFont = defaultFont; } public void setAlignment(int align) { ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); RichTextView Notice that the default layout is FlowLayout we rely on that to allow aligning the text to the center/right
  • 11. setText(text); } private void init() { defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize); boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize); italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize); sizeOfSpace = defaultFont.charWidth(' '); currentFont = defaultFont; } public void setAlignment(int align) { ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); RichTextView This method is invoked by the parser to add a component
  • 12. setText(text); } private void init() { defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT, fontSize); boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize); italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize); sizeOfSpace = defaultFont.charWidth(' '); currentFont = defaultFont; } public void setAlignment(int align) { ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); RichTextView If the string has spaces we split it to multiple separate strings so each individual component can break a line in the flow layout
  • 13. ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); RichTextView We add either a Label or a Button that looks like a Label
  • 14. ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); RichTextView The parser updates currentLink so if we have a link we are within an <a> tag and need to use a button styled to look like a link
  • 15. ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); RichTextView currentLinkValue is the content of href we just fire the listener event dispatcher with the href content as the source
  • 16. ((FlowLayout)getLayout()).setAlign(align); } private void createComponent(String t) { if(t.indexOf(' ') > -1) { for(String s : StringUtil.tokenize(t, ' ')) { createComponent(s); } return; } Label l; if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); RichTextView If this isn't a link it's label
  • 17. if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); s.setMargin(0, 0, 0, 0); lastCmp = s; add(l); } public final void setText(String text) { this.text = text; removeAll(); try { char[] chrs = ("<body>" + text + "</body>").toCharArray(); new Parser().eventParser(new CharArrayReader(chrs)); RichTextView We remove the spaces, padding & margin. We then use the width of the a space to re-add a space in the form of padding. This allows line breaks on word boundaries
  • 18. if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); s.setMargin(0, 0, 0, 0); lastCmp = s; add(l); } public final void setText(String text) { this.text = text; removeAll(); try { char[] chrs = ("<body>" + text + "</body>").toCharArray(); new Parser().eventParser(new CharArrayReader(chrs)); RichTextView The parser might need to remove a space for some special cases e.g. if we close an </a> tag and place a dot right next to it
  • 19. if(currentLink != null) { Button b = new Button(t, "Label"); final String currentLinkValue = currentLink; b.addActionListener(e -> listeners.fireActionEvent( new ActionEvent(currentLinkValue))); l = b; } else { l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); s.setMargin(0, 0, 0, 0); lastCmp = s; add(l); } public final void setText(String text) { this.text = text; removeAll(); try { char[] chrs = ("<body>" + text + "</body>").toCharArray(); new Parser().eventParser(new CharArrayReader(chrs)); RichTextView The setText method is the API that sets the HTML to this component
  • 20. l = new Label(t); } Style s = l.getAllStyles(); s.setFont(currentFont); s.setFgColor(currentColor); s.setPaddingUnit(Style.UNIT_TYPE_PIXELS); s.setPadding(0, 0, 0, sizeOfSpace); s.setMargin(0, 0, 0, 0); lastCmp = s; add(l); } public final void setText(String text) { this.text = text; removeAll(); try { char[] chrs = ("<body>" + text + "</body>").toCharArray(); new Parser().eventParser(new CharArrayReader(chrs)); } catch(IOException err) { log(err); } } public String getText() { return text; } RichTextView We wrap the HTML so it's well formed and parse the text using the Parser inner class
  • 21. this.text = text; removeAll(); try { char[] chrs = ("<body>" + text + "</body>").toCharArray(); new Parser().eventParser(new CharArrayReader(chrs)); } catch(IOException err) { log(err); } } public String getText() { return text; } public void addLinkListener(ActionListener al) { listeners.addListener(al); } public void removeLinkListener(ActionListener al) { listeners.removeListener(al); } class Parser extends XMLParser { @Override protected void textElement(String text) { RichTextView These add listeners so when a user clicks on a link these listeners will receive the event from the event dispatcher
  • 22. listeners.addListener(al); } public void removeLinkListener(ActionListener al) { listeners.removeListener(al); } class Parser extends XMLParser { @Override protected void textElement(String text) { if(text.length() > 0) { if(lastCmp != null && text.startsWith(" ")) { lastCmp.setPadding(0, 0, 0, sizeOfSpace); } createComponent(text); if(!text.endsWith(" ")) { lastCmp.setPadding(0, 0, 0, 0); } } } @Override protected boolean startTag(String tag) { switch(tag.toLowerCase()) { case "a": currentColor = 0x4267B2; RichTextView The next stage is the Parser inner class, it's a bit big . We derive XMLParser & override callback methods to tokenize the string
  • 23. listeners.addListener(al); } public void removeLinkListener(ActionListener al) { listeners.removeListener(al); } class Parser extends XMLParser { @Override protected void textElement(String text) { if(text.length() > 0) { if(lastCmp != null && text.startsWith(" ")) { lastCmp.setPadding(0, 0, 0, sizeOfSpace); } createComponent(text); if(!text.endsWith(" ")) { lastCmp.setPadding(0, 0, 0, 0); } } } @Override protected boolean startTag(String tag) { switch(tag.toLowerCase()) { case "a": currentColor = 0x4267B2; RichTextView All the methods here are callbacks in a SAX style parser. We also support a DOM style but I chose a callback approach for performance
  • 24. listeners.addListener(al); } public void removeLinkListener(ActionListener al) { listeners.removeListener(al); } class Parser extends XMLParser { @Override protected void textElement(String text) { if(text.length() > 0) { if(lastCmp != null && text.startsWith(" ")) { lastCmp.setPadding(0, 0, 0, sizeOfSpace); } createComponent(text); if(!text.endsWith(" ")) { lastCmp.setPadding(0, 0, 0, 0); } } } @Override protected boolean startTag(String tag) { switch(tag.toLowerCase()) { case "a": currentColor = 0x4267B2; RichTextView If the block element starts with a space we add a space to the last block element
  • 25. listeners.addListener(al); } public void removeLinkListener(ActionListener al) { listeners.removeListener(al); } class Parser extends XMLParser { @Override protected void textElement(String text) { if(text.length() > 0) { if(lastCmp != null && text.startsWith(" ")) { lastCmp.setPadding(0, 0, 0, sizeOfSpace); } createComponent(text); if(!text.endsWith(" ")) { lastCmp.setPadding(0, 0, 0, 0); } } } @Override protected boolean startTag(String tag) { switch(tag.toLowerCase()) { case "a": currentColor = 0x4267B2; RichTextView We remove the space from the previous component if the block doesn't end with a space
  • 26. } createComponent(text); if(!text.endsWith(" ")) { lastCmp.setPadding(0, 0, 0, 0); } } } @Override protected boolean startTag(String tag) { switch(tag.toLowerCase()) { case "a": currentColor = 0x4267B2; break; case "b": currentFont = boldFont; break; case "i": currentFont = italicFont; break; } return true; } @Override protected void endTag(String tag) { RichTextView Here we handle the individual HTML tags we want to support by changing the current font or color
  • 27. currentColor = 0x4267B2; break; case "b": currentFont = boldFont; break; case "i": currentFont = italicFont; break; } return true; } @Override protected void endTag(String tag) { currentColor = 0; currentLink = null; currentFont = defaultFont; } @Override protected void attribute( String tag, String attributeName, String value) { if(tag.toLowerCase().equals("a") && attributeName.toLowerCase().equals("href")) { currentLink = value; } RichTextView When a tag ends we clear the current font/color and restore the default, we don't handle complexity like tag nesting etc.
  • 28. } @Override protected void endTag(String tag) { currentColor = 0; currentLink = null; currentFont = defaultFont; } @Override protected void attribute( String tag, String attributeName, String value) { if(tag.toLowerCase().equals("a") && attributeName.toLowerCase().equals("href")) { currentLink = value; } } @Override protected void notifyError(int errorId, String tag, String attribute, String value, String description) { log("Error during parsing: " + tag); } } } RichTextView When we're in an <a> tag we save the value of the href attribute for use in the event handler code. That's it, we now have a poor mans HTML renderer that we can use for simple blocks of highlighted text. We'll make use of it soon...