SlideShare a Scribd company logo
Creating a Facebook Clone - Part IV
The next step is the login form which requires a bit more... The design of the login UI varies dramatically and in both cases it wasn't great. So I ended up taking a great
deal of creative liberty in my interpretation on how this "should look".
© Codename One 2017 all rights reserved
There are a lot of things I chose to change about the UI, most of them are due to taste and personal preference. I tried to make the code as close as possible so I would
reuse more of it but I still wanted to show how I can create very different UI's for very different platforms.
© Codename One 2017 all rights reserved
E.g. this is the iOS version running on a tablet, you will notice I copied a lot of the UI but you will also notice I took some inspiration from Android.
© Codename One 2017 all rights reserved
I made sure to get everything working in landscape, notice I took a very different approach to the UI in landscape that I think looks MUCH better than the native version
which frankly looks awful. 

Notice that in the iPad version in the clone I don’t move the title image doesn't move to the side like it does in other platforms when shifting to landscape.

I also ignored the top area with more functionality & languages. It doesn't exist on iOS & didn't strike me as too important
© Codename One 2017 all rights reserved
Since the login form is a complex form I chose to create a new class. For that I also chose to create a new package: com.codename1.fbclone.forms.

This class is a bit big but before I get into it I'd like to explain how I implemented this UI in the iOS version. 

What we see here are two text fields grouped together so the first and the last have a rounded look while entries in the middle (which we don't have here) would not. This
is a common look in iOS although it has gone somewhat out of fashion.

In Codename One we can apply that look using the ComponentGroup class. This class is a BoxLayout container that automatically assigns special UIID's to components
so the first Component would be GroupElementFirst, the second would be GroupElement & the last would be GroupElementLast.

Notice that there is also a special UIID for button's added to the group. We don’t use it in this case so that’s just an FYI.

The iOS theme includes styling for these elements, they include rounding on the top for the first element and rounding on the bottom for the last element.

On Android this design paradigm is uncommon. As a result there is no styling for these elements on Android. The group layout acts as a regular box layout container on
Android and doesn't change the natural styling of the elements. This works seamlessly because the native iOS theme has a theme constant: ComponentGroupBool. If it
is set to true that means that the UIID's should be changed.

ComponentGroupBool is only defined on iOS and defaults to false elsewhere...

In the code I use that theme constant to decide whether to show the Android UI or the iOS UI…
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
Lets get into the code first the class, variable & method declarations.

The logo is pretty much the same value we saw for the splash screen, it doesn't have to be the same though
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
E-Mail & Password are just standard input fields
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
The following buttons exist in the iOS & Android UI's although the signUp button has different text in the Android version
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
The login form uses border layout so we can place the content where we want.

Without the UIID the toolbar would appear on the top of the screen. Container is transparent with 0 padding/margin which hides the toolbar
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
We set the name of the logo so the logo from the splash screen will animate into place due to the MorphTransition
public class LoginForm extends Form {
private Label logo =
new Label("uf308", "IconFont");
private TextField user = new TextField("", "Email or Phone", 30,
TextField.EMAILADDR);
private TextField password = new TextField("", "Password", 30,
TextField.PASSWORD);
private Button login = new Button("Log In");
private Button signUp = new Button("Sign Up for Facebook");
private Button needHelp = new Button("Need Help?");
public LoginForm() {
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
LoginForm
We're placing the logo on the top of the screen in the center, notice I use the LoginTitle to set the background around the logo
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
If we are on a phone I want the logo container to shift to the right side of the screen when rotating to landscape this is handled automatically by BorderLayout
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
If we have ComponentGroup support I'll show the iOS style UI, I prefer this to testing whether this is "iOS" since other platforms might choose to apply that look
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
We place the UI in a the absolute center so it's in the middle of the content. We wrap the button and ComponentGroup in a box layout on the Y axis so they stick
together in the center.

The login & password go into the ComponentGroup & the login button is placed below.
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
The PaddedContainer UIID allows us to create some whitespace around the components so they don't stick to the edges when running in a phone
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
I add the content pane to the center of the UI so it will take up the available space
super(new BorderLayout());
getToolbar().setUIID("Container");
logo.setName("Logo");
Container logoContainer = BorderLayout.centerAbsolute(logo);
logoContainer.setUIID("LoginTitle");
add(NORTH, logoContainer);
if(!isTablet()) {
BorderLayout bl = ((BorderLayout)getLayout());
bl.defineLandscapeSwap(NORTH, EAST);
}
if(getUIManager().
isThemeConstant("ComponentGroupBool", false)) {
Container content = BorderLayout.centerAbsolute(
BoxLayout.encloseY(
ComponentGroup.enclose(user, password),
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
LoginForm
We place the two signup buttons on the bottom of the UI
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
Button forgotPassword = new Button("Forgot Password", "BlueText");
signUp.setUIID("GreenButton");
signUp.setText("Create new Facebook Account");
Container cnt = BoxLayout.encloseY(
user, password, login, forgotPassword, signUp);
cnt.setUIID("PaddedContainer");
cnt.setScrollableY(true);
add(CENTER, cnt);
}
}
protected boolean shouldPaintStatusBar() {
return false;
}
}
LoginForm
The Android version is far simpler, we place the components in a box layout Y and tweak a few UIID's/texts
login));
content.setUIID("PaddedContainer");
add(CENTER, content);
login.setUIID("BlueButtonOnBlueBackground");
setUIID("SplashForm");
signUp.setUIID("WhiteLinkButton");
needHelp.setUIID("WhiteLinkButton");
content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp));
} else {
login.setUIID("BlueButton");
Button forgotPassword = new Button("Forgot Password", "BlueText");
signUp.setUIID("GreenButton");
signUp.setText("Create new Facebook Account");
Container cnt = BoxLayout.encloseY(
user, password, login, forgotPassword, signUp);
cnt.setUIID("PaddedContainer");
cnt.setScrollableY(true);
add(CENTER, cnt);
}
}
protected boolean shouldPaintStatusBar() {
return false;
}
}
LoginForm
And finally this hides the status bar line on the top of the form and lets the image occupy that space. The status bar appears on top of the Toolbar and grabs space to
push down the content to leave space for the iPhone X notch and iOS clock etc. It’s a feature of iOS only.
Why we Need the Content Container
© Codename One 2017 all rights reserved
One item I skimmed over when reviewing the iOS look code is why we need the content container? 

We could have just used the form which is already in BorderLayout and just changed it to use CENTER_BEHAVIOR_CENTER_ABSOLUTE. That would have worked for
portrait but not for landscape…

Since south takes up the space below EAST & WEST it would have produced this image which isn’t what we want.
#Constants {
includeNativeBool: true;
scrollVisibleBool: false;
labelGap: 2;
capsButtonUiids:"BlueText,GreenButton,BlueButton";
}
theme.css
The next big thing is setting the CSS values. Before I go into the new UIID’s we'll start with one change to an existing CSS selector. This allows the buttons in Android to
appear in all caps. 

These are the UIID's that should be considered as buttons. 

Since the Button class is very common we don't apply the caps behavior to all buttons by default. If the UIID isn't Button we need to explicitly declare that we are
interested in the text becoming uppercase. Notice this will only apply to platforms where buttons are caps to begin with so if we use BlueText on iOS it won't be
capitalized.
© Codename One 2017 all rights reserved
Before we go into the full set of CSS changes here is a map of the UIID’s I’ll mention. You can reference this map if something isn’t clear when I review the code.
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
We need to add the following to the CSS file to support all the new UIID's, there is quite a lot of code here…

This automatically adds the file to the Resources the login banner is this jpeg which you should place in css/images in your project
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
By default the plugin adds multi-images where we can specify the DPI of the source image. By setting the DPI to 0 I state I want a regular image.

Multi Images are great but for a background that would be scaled to fill anyway the pixel perfect scaling of multi-images will probably be wasted.

Multi Images take extra space in the resource file and thus in ram. JPEG is especially efficient in terms of size and for relatively large background images it can be the
ideal option.
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
The title image fills available space which should look decent regardless of the resolution for this type of image
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
I use a pretty big padding value to give the title area space in both orientations so the image background will show
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
We shouldn't change the Container UIID so I added this generic UIID for spacing out the UI a bit
LoginTitle {
background-image: url(images/login-banner.jpg);
cn1-source-dpi: 0;
cn1-background-type: cn1-image-scaled-fill;
margin: 0px;
padding: 8mm;
}
PaddedContainer {
padding: 3mm;
margin: 0mm;
background: transparent;
}
BaseButton {
text-align: center;
font-family: "native:MainRegular";
font-size: 2.6mm;
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
theme.css
I use this as the base UIID for buttons as they have a lot of properties in common such as center alignment etc.

I usually prefer to derive UIID's from my own style and avoid deriving from builtin UIID's as those might change in unpredictable ways.
padding: 1.4mm;
margin: 2mm;
color: white;
}
BlueButtonOnBlueBackground {
border-radius: 1mm;
background: #587EBE;
cn1-derive: BaseButton;
}
BlueButton {
background: #587EBE;
font-family: "native:MainBold";
cn1-derive: BaseButton;
}
BlueText {
cn1-derive: BaseButton;
font-family: "native:MainBold";
color: #385898;
background: transparent;
}
GreenButton {
cn1-derive: BaseButton;
font-family: "native:MainBold";
background: #00A400;
}
theme.css
We derive the base button UIID. We customize the background color & use a round rect for the border. This pattern repeats in almost all of the following UIID's
background: transparent;
}
GreenButton {
cn1-derive: BaseButton;
font-family: "native:MainBold";
background: #00A400;
}
WhiteLinkButton {
cn1-derive: BaseButton;
font-family: "native:MainLight";
margin: 0px;
}
TextHint {
font-family: "native:MainLight";
font-size: 2.6mm;
padding: 2mm;
}
TextField {
margin: 2mm;
}
GroupElementFirst {
padding: 3mm;
}
GroupElementLast {
padding: 3mm;
}
theme.css
The default TextHint looked bad so we need to customize it to have better fonts/padding. This is the text that appears before we type in the text field
GreenButton {
cn1-derive: BaseButton;
font-family: "native:MainBold";
background: #00A400;
}
WhiteLinkButton {
cn1-derive: BaseButton;
font-family: "native:MainLight";
margin: 0px;
}
TextHint {
font-family: "native:MainLight";
font-size: 2.6mm;
padding: 2mm;
}
TextField {
margin: 2mm;
}
GroupElementFirst {
padding: 3mm;
}
GroupElementLast {
padding: 3mm;
}
theme.css
Additional padding/margin is needed in the text field & the group elements to make them fit better into the UI
public static void showLoginForm() {
new LoginForm().show();
}
UIController
With these changes we are nearly done. There is just one last change to make this all work. We just create and show the login form for now, no extra logic

With that we can run the code and see the UI running.

Next I'll continue with the signup process…

More Related Content

PDF
Creating a Facebook Clone - Part IV.pdf
PDF
Creating a Facebook Clone - Part III - Transcript.pdf
PDF
Creating a Facebook Clone - Part VI - Transcript.pdf
PDF
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
PDF
Creating a Facebook Clone - Part I - Transcript.pdf
PDF
Creating a Facebook Clone - Part III.pdf
PDF
Creating a Facebook Clone - Part XXVIII.pdf
PDF
Creating a Facebook Clone - Part VIII - Transcript.pdf
Creating a Facebook Clone - Part IV.pdf
Creating a Facebook Clone - Part III - Transcript.pdf
Creating a Facebook Clone - Part VI - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part I - Transcript.pdf
Creating a Facebook Clone - Part III.pdf
Creating a Facebook Clone - Part XXVIII.pdf
Creating a Facebook Clone - Part VIII - Transcript.pdf

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

PDF
Creating a Facebook Clone - Part VI.pdf
PDF
Adapting to Tablets and Desktops - Part 1 - Transcript.pdf
PDF
07_UIAndroid.pdf
PDF
Creating a Facebook Clone - Part VII.pdf
PDF
Creating an Uber Clone - Part XXXVI - Transcript.pdf
PDF
create-netflix-clone-06-client-ui_transcript.pdf
PDF
Creating an Uber Clone - Part V - Transcript.pdf
PDF
Creating a Facebook Clone - Part VIII.pdf
PPTX
Nokia Asha App Development - Part 2
PDF
How do I - Create a List of Items - Transcript.pdf
PDF
Creating a Facebook Clone - Part XXX - Transcript.pdf
PDF
Creating an Uber - Part VI - Transcript.pdf
PDF
Creating an Uber Clone - Part III - Transcript.pdf
PDF
Android App Development 03 : Widget & UI
PDF
Extracting ui Design - part 6 - transcript.pdf
PDF
MobileAppDev Handout#2
PDF
Creating a Facebook Clone - Part XVI - Transcript.pdf
PDF
themes-how-do-i - Transcript.pdf
PDF
Creating an Uber Clone - Part XXXX - Transcript.pdf
PDF
Android UI Fundamentals part 1
Creating a Facebook Clone - Part VI.pdf
Adapting to Tablets and Desktops - Part 1 - Transcript.pdf
07_UIAndroid.pdf
Creating a Facebook Clone - Part VII.pdf
Creating an Uber Clone - Part XXXVI - Transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
Creating an Uber Clone - Part V - Transcript.pdf
Creating a Facebook Clone - Part VIII.pdf
Nokia Asha App Development - Part 2
How do I - Create a List of Items - Transcript.pdf
Creating a Facebook Clone - Part XXX - Transcript.pdf
Creating an Uber - Part VI - Transcript.pdf
Creating an Uber Clone - Part III - Transcript.pdf
Android App Development 03 : Widget & UI
Extracting ui Design - part 6 - transcript.pdf
MobileAppDev Handout#2
Creating a Facebook Clone - Part XVI - Transcript.pdf
themes-how-do-i - Transcript.pdf
Creating an Uber Clone - Part XXXX - Transcript.pdf
Android UI Fundamentals part 1
Ad

More from ShaiAlmog1 (20)

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

Recently uploaded (20)

PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Empathic Computing: Creating Shared Understanding
PPTX
Spectroscopy.pptx food analysis technology
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PPTX
Big Data Technologies - Introduction.pptx
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Approach and Philosophy of On baking technology
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Encapsulation theory and applications.pdf
PDF
KodekX | Application Modernization Development
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PPTX
Cloud computing and distributed systems.
PDF
NewMind AI Weekly Chronicles - August'25 Week I
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Review of recent advances in non-invasive hemoglobin estimation
Empathic Computing: Creating Shared Understanding
Spectroscopy.pptx food analysis technology
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Dropbox Q2 2025 Financial Results & Investor Presentation
Big Data Technologies - Introduction.pptx
Encapsulation_ Review paper, used for researhc scholars
Approach and Philosophy of On baking technology
Reach Out and Touch Someone: Haptics and Empathic Computing
Chapter 3 Spatial Domain Image Processing.pdf
Network Security Unit 5.pdf for BCA BBA.
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
The Rise and Fall of 3GPP – Time for a Sabbatical?
Spectral efficient network and resource selection model in 5G networks
Encapsulation theory and applications.pdf
KodekX | Application Modernization Development
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
Cloud computing and distributed systems.
NewMind AI Weekly Chronicles - August'25 Week I

Creating a Facebook Clone - Part IV - Transcript.pdf

  • 1. Creating a Facebook Clone - Part IV The next step is the login form which requires a bit more... The design of the login UI varies dramatically and in both cases it wasn't great. So I ended up taking a great deal of creative liberty in my interpretation on how this "should look".
  • 2. © Codename One 2017 all rights reserved There are a lot of things I chose to change about the UI, most of them are due to taste and personal preference. I tried to make the code as close as possible so I would reuse more of it but I still wanted to show how I can create very different UI's for very different platforms.
  • 3. © Codename One 2017 all rights reserved E.g. this is the iOS version running on a tablet, you will notice I copied a lot of the UI but you will also notice I took some inspiration from Android.
  • 4. © Codename One 2017 all rights reserved I made sure to get everything working in landscape, notice I took a very different approach to the UI in landscape that I think looks MUCH better than the native version which frankly looks awful. Notice that in the iPad version in the clone I don’t move the title image doesn't move to the side like it does in other platforms when shifting to landscape. I also ignored the top area with more functionality & languages. It doesn't exist on iOS & didn't strike me as too important
  • 5. © Codename One 2017 all rights reserved Since the login form is a complex form I chose to create a new class. For that I also chose to create a new package: com.codename1.fbclone.forms. This class is a bit big but before I get into it I'd like to explain how I implemented this UI in the iOS version. What we see here are two text fields grouped together so the first and the last have a rounded look while entries in the middle (which we don't have here) would not. This is a common look in iOS although it has gone somewhat out of fashion. In Codename One we can apply that look using the ComponentGroup class. This class is a BoxLayout container that automatically assigns special UIID's to components so the first Component would be GroupElementFirst, the second would be GroupElement & the last would be GroupElementLast. Notice that there is also a special UIID for button's added to the group. We don’t use it in this case so that’s just an FYI. The iOS theme includes styling for these elements, they include rounding on the top for the first element and rounding on the bottom for the last element. On Android this design paradigm is uncommon. As a result there is no styling for these elements on Android. The group layout acts as a regular box layout container on Android and doesn't change the natural styling of the elements. This works seamlessly because the native iOS theme has a theme constant: ComponentGroupBool. If it is set to true that means that the UIID's should be changed. ComponentGroupBool is only defined on iOS and defaults to false elsewhere... In the code I use that theme constant to decide whether to show the Android UI or the iOS UI…
  • 6. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm Lets get into the code first the class, variable & method declarations. The logo is pretty much the same value we saw for the splash screen, it doesn't have to be the same though
  • 7. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm E-Mail & Password are just standard input fields
  • 8. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm The following buttons exist in the iOS & Android UI's although the signUp button has different text in the Android version
  • 9. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm The login form uses border layout so we can place the content where we want. Without the UIID the toolbar would appear on the top of the screen. Container is transparent with 0 padding/margin which hides the toolbar
  • 10. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm We set the name of the logo so the logo from the splash screen will animate into place due to the MorphTransition
  • 11. public class LoginForm extends Form { private Label logo = new Label("uf308", "IconFont"); private TextField user = new TextField("", "Email or Phone", 30, TextField.EMAILADDR); private TextField password = new TextField("", "Password", 30, TextField.PASSWORD); private Button login = new Button("Log In"); private Button signUp = new Button("Sign Up for Facebook"); private Button needHelp = new Button("Need Help?"); public LoginForm() { super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); LoginForm We're placing the logo on the top of the screen in the center, notice I use the LoginTitle to set the background around the logo
  • 12. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm If we are on a phone I want the logo container to shift to the right side of the screen when rotating to landscape this is handled automatically by BorderLayout
  • 13. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm If we have ComponentGroup support I'll show the iOS style UI, I prefer this to testing whether this is "iOS" since other platforms might choose to apply that look
  • 14. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm We place the UI in a the absolute center so it's in the middle of the content. We wrap the button and ComponentGroup in a box layout on the Y axis so they stick together in the center. The login & password go into the ComponentGroup & the login button is placed below.
  • 15. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm The PaddedContainer UIID allows us to create some whitespace around the components so they don't stick to the edges when running in a phone
  • 16. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm I add the content pane to the center of the UI so it will take up the available space
  • 17. super(new BorderLayout()); getToolbar().setUIID("Container"); logo.setName("Logo"); Container logoContainer = BorderLayout.centerAbsolute(logo); logoContainer.setUIID("LoginTitle"); add(NORTH, logoContainer); if(!isTablet()) { BorderLayout bl = ((BorderLayout)getLayout()); bl.defineLandscapeSwap(NORTH, EAST); } if(getUIManager(). isThemeConstant("ComponentGroupBool", false)) { Container content = BorderLayout.centerAbsolute( BoxLayout.encloseY( ComponentGroup.enclose(user, password), login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); LoginForm We place the two signup buttons on the bottom of the UI
  • 18. login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); Button forgotPassword = new Button("Forgot Password", "BlueText"); signUp.setUIID("GreenButton"); signUp.setText("Create new Facebook Account"); Container cnt = BoxLayout.encloseY( user, password, login, forgotPassword, signUp); cnt.setUIID("PaddedContainer"); cnt.setScrollableY(true); add(CENTER, cnt); } } protected boolean shouldPaintStatusBar() { return false; } } LoginForm The Android version is far simpler, we place the components in a box layout Y and tweak a few UIID's/texts
  • 19. login)); content.setUIID("PaddedContainer"); add(CENTER, content); login.setUIID("BlueButtonOnBlueBackground"); setUIID("SplashForm"); signUp.setUIID("WhiteLinkButton"); needHelp.setUIID("WhiteLinkButton"); content.add(SOUTH, FlowLayout.encloseCenter(signUp, needHelp)); } else { login.setUIID("BlueButton"); Button forgotPassword = new Button("Forgot Password", "BlueText"); signUp.setUIID("GreenButton"); signUp.setText("Create new Facebook Account"); Container cnt = BoxLayout.encloseY( user, password, login, forgotPassword, signUp); cnt.setUIID("PaddedContainer"); cnt.setScrollableY(true); add(CENTER, cnt); } } protected boolean shouldPaintStatusBar() { return false; } } LoginForm And finally this hides the status bar line on the top of the form and lets the image occupy that space. The status bar appears on top of the Toolbar and grabs space to push down the content to leave space for the iPhone X notch and iOS clock etc. It’s a feature of iOS only.
  • 20. Why we Need the Content Container © Codename One 2017 all rights reserved One item I skimmed over when reviewing the iOS look code is why we need the content container? We could have just used the form which is already in BorderLayout and just changed it to use CENTER_BEHAVIOR_CENTER_ABSOLUTE. That would have worked for portrait but not for landscape… Since south takes up the space below EAST & WEST it would have produced this image which isn’t what we want.
  • 21. #Constants { includeNativeBool: true; scrollVisibleBool: false; labelGap: 2; capsButtonUiids:"BlueText,GreenButton,BlueButton"; } theme.css The next big thing is setting the CSS values. Before I go into the new UIID’s we'll start with one change to an existing CSS selector. This allows the buttons in Android to appear in all caps. These are the UIID's that should be considered as buttons. Since the Button class is very common we don't apply the caps behavior to all buttons by default. If the UIID isn't Button we need to explicitly declare that we are interested in the text becoming uppercase. Notice this will only apply to platforms where buttons are caps to begin with so if we use BlueText on iOS it won't be capitalized.
  • 22. © Codename One 2017 all rights reserved Before we go into the full set of CSS changes here is a map of the UIID’s I’ll mention. You can reference this map if something isn’t clear when I review the code.
  • 23. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css We need to add the following to the CSS file to support all the new UIID's, there is quite a lot of code here… This automatically adds the file to the Resources the login banner is this jpeg which you should place in css/images in your project
  • 24. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css By default the plugin adds multi-images where we can specify the DPI of the source image. By setting the DPI to 0 I state I want a regular image. Multi Images are great but for a background that would be scaled to fill anyway the pixel perfect scaling of multi-images will probably be wasted. Multi Images take extra space in the resource file and thus in ram. JPEG is especially efficient in terms of size and for relatively large background images it can be the ideal option.
  • 25. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css The title image fills available space which should look decent regardless of the resolution for this type of image
  • 26. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css I use a pretty big padding value to give the title area space in both orientations so the image background will show
  • 27. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css We shouldn't change the Container UIID so I added this generic UIID for spacing out the UI a bit
  • 28. LoginTitle { background-image: url(images/login-banner.jpg); cn1-source-dpi: 0; cn1-background-type: cn1-image-scaled-fill; margin: 0px; padding: 8mm; } PaddedContainer { padding: 3mm; margin: 0mm; background: transparent; } BaseButton { text-align: center; font-family: "native:MainRegular"; font-size: 2.6mm; padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; theme.css I use this as the base UIID for buttons as they have a lot of properties in common such as center alignment etc. I usually prefer to derive UIID's from my own style and avoid deriving from builtin UIID's as those might change in unpredictable ways.
  • 29. padding: 1.4mm; margin: 2mm; color: white; } BlueButtonOnBlueBackground { border-radius: 1mm; background: #587EBE; cn1-derive: BaseButton; } BlueButton { background: #587EBE; font-family: "native:MainBold"; cn1-derive: BaseButton; } BlueText { cn1-derive: BaseButton; font-family: "native:MainBold"; color: #385898; background: transparent; } GreenButton { cn1-derive: BaseButton; font-family: "native:MainBold"; background: #00A400; } theme.css We derive the base button UIID. We customize the background color & use a round rect for the border. This pattern repeats in almost all of the following UIID's
  • 30. background: transparent; } GreenButton { cn1-derive: BaseButton; font-family: "native:MainBold"; background: #00A400; } WhiteLinkButton { cn1-derive: BaseButton; font-family: "native:MainLight"; margin: 0px; } TextHint { font-family: "native:MainLight"; font-size: 2.6mm; padding: 2mm; } TextField { margin: 2mm; } GroupElementFirst { padding: 3mm; } GroupElementLast { padding: 3mm; } theme.css The default TextHint looked bad so we need to customize it to have better fonts/padding. This is the text that appears before we type in the text field
  • 31. GreenButton { cn1-derive: BaseButton; font-family: "native:MainBold"; background: #00A400; } WhiteLinkButton { cn1-derive: BaseButton; font-family: "native:MainLight"; margin: 0px; } TextHint { font-family: "native:MainLight"; font-size: 2.6mm; padding: 2mm; } TextField { margin: 2mm; } GroupElementFirst { padding: 3mm; } GroupElementLast { padding: 3mm; } theme.css Additional padding/margin is needed in the text field & the group elements to make them fit better into the UI
  • 32. public static void showLoginForm() { new LoginForm().show(); } UIController With these changes we are nearly done. There is just one last change to make this all work. We just create and show the login form for now, no extra logic With that we can run the code and see the UI running. Next I'll continue with the signup process…