SlideShare a Scribd company logo
1 of 30
Download to read offline
Creating a Netflix Clone
VI
For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms
either so this isn’t too different from the original.

The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough

CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
codenameone.com github.com/codenameone/CodenameOne
Client UI
For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms
either so this isn’t too different from the original.

The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough

CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
codenameone.com github.com/codenameone/CodenameOne
Client UI
There are just three forms in the demo
We’re using layered toolbar for the UI
CSS is used for the UI design
For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms
either so this isn’t too different from the original.

The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough

CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
#Constants {
includeNativeBool: true;
labelGap: 1;
}
Form {
background: black;
}
TitleCommand {
color: white;
}
ToolbarGradient {
border: none;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6));
padding: 2mm;
}
ThumbIcon {
background: transparent;
margin: 0px;
padding: 8mm;
padding-right: 3mm;
}
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
I won’t go over the entire CSS and will instead go back to it when introducing specific UIIDs. I will cover the generic concepts first though.

We have only two constants in the CSS. The first is the include native feature which should be on by default always. The second is a standard label gap between the label
and an icon. I think 1 millimeter is generally a good number here.
public class BaseForm extends Form {
public BaseForm(Layout l) {
super(l);
}
@Override
protected void initGlobalToolbar() {
Toolbar tb = new Toolbar(true);
setToolbar(tb);
tb.setUIID("ToolbarGradient");
}
}
Source Listing - BaseForm
codenameone.com github.com/codenameone/CodenameOne
We don’t have too much in common between the forms at this level of the UI so there’s only one method in common.

The initGlobalToolbar method initializes the toolbar component when global toolbar is turned on (which is the default). We do two things here. We set the toolbar to use
the layered mode. We do that by passing true to the toolbar constructor. 

Next we set the UIID of the Toolbar to ToolbarGradient which we use to indicate the translucent gradient background to separate the toolbar from the content
}
Form {
background: black;
}
TitleCommand {
color: white;
}
ToolbarGradient {
border: none;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6));
padding: 2mm;
}
ThumbIcon {
background: transparent;
margin: 0px;
padding: 8mm;
padding-right: 3mm;
}
HeroImageLogo {
/* margin: 35% 22% 5mm 22%; */
margin: 35mm 7mm 5mm 7mm;
padding: 3.5mm;
background: transparent;
}
HeroImagePlayButton {
border: none;
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The toolbar gradient is a gradient in black between 0.6 alpha to almost clear alpha. This creates a slight fade effect over the title area so the title will still be visible if the
image in the background has the same colors. 

Codename One doesn’t currently support alpha gradients. As a solution the CSS support generates an image of the gradient during build and uses that
public class SplashForm extends Form {
private SplashForm() {
super(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER));
}
private void init() {
Resources r = Resources.getGlobalResources();
add(CENTER, new Label(r.getImage("netflix-logo.png")));
}
public static SplashForm create() {
SplashForm h = new SplashForm();
h.init();
return h;
}
}
Source Listing - SplashForm
codenameone.com github.com/codenameone/CodenameOne
The splash form is stupid simple. We just place the logo in the center of the form and initialize. There’s only one thing to discuss here and that’s the source of the netflix-
logo.png file.
font-size: 2.1mm;
border: none;
background-color: black;
padding: 1.5mm 0.5mm 1.5mm 0.5mm;
margin: 0px;
}
/* =============== IMAGES ============= */
ImageImports1 {
background-image: url(images/data/show-logo.png),
url(images/data/hero-background.jpg),
url(images/data/thumb1.jpg),
url(images/data/thumb2.jpg),
url(images/data/thumb3.jpg),
url(images/data/thumb4.jpg),
url(images/data/thumb5.jpg),
url(images/data/thumb6.jpg),
url(images/data/thumb7.jpg),
url(images/data/thumb8.jpg);
cn1-source-dpi: 0;
}
ImageImports2 {
background-image: url(images/netflix-logo.png);
cn1-source-dpi: 320;
}
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
All the images are declared in the CSS under two dummy UIIDs specifically ImageImports1 and ImageImports2. This just pulls all the images into the resource file so we
can later make use of them.
font-size: 2.1mm;
border: none;
background-color: black;
padding: 1.5mm 0.5mm 1.5mm 0.5mm;
margin: 0px;
}
/* =============== IMAGES ============= */
ImageImports1 {
background-image: url(images/data/show-logo.png),
url(images/data/hero-background.jpg),
url(images/data/thumb1.jpg),
url(images/data/thumb2.jpg),
url(images/data/thumb3.jpg),
url(images/data/thumb4.jpg),
url(images/data/thumb5.jpg),
url(images/data/thumb6.jpg),
url(images/data/thumb7.jpg),
url(images/data/thumb8.jpg);
cn1-source-dpi: 0;
}
ImageImports2 {
background-image: url(images/netflix-logo.png);
cn1-source-dpi: 320;
}
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The source DPI is the reason we have to image import UIIDs. When source DPI is set to 0 it means we want the image imported “as is”. Not as a multi-image but rather
as a single image.
font-size: 2.1mm;
border: none;
background-color: black;
padding: 1.5mm 0.5mm 1.5mm 0.5mm;
margin: 0px;
}
/* =============== IMAGES ============= */
ImageImports1 {
background-image: url(images/data/show-logo.png),
url(images/data/hero-background.jpg),
url(images/data/thumb1.jpg),
url(images/data/thumb2.jpg),
url(images/data/thumb3.jpg),
url(images/data/thumb4.jpg),
url(images/data/thumb5.jpg),
url(images/data/thumb6.jpg),
url(images/data/thumb7.jpg),
url(images/data/thumb8.jpg);
cn1-source-dpi: 0;
}
ImageImports2 {
background-image: url(images/netflix-logo.png);
cn1-source-dpi: 320;
}
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The logo is the only image where we’re interested in multi-image behavior. Since most images come from the server we don’t need many multi-images in the app.
public class HomeForm extends BaseForm {
private HomeForm() {
super(new BorderLayout());
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The HomeForm is the main UI of the application listing the content that the user can select from. It’s a base form which means the title of the form is overlaid on the
content and it uses border layout.
add(CENTER, tabs);
}
private Container movieList(List<Content> lst) {
Container p = new Container(BoxLayout.x());
for(Content c : lst) {
ScaleImageButton b = new ScaleImageButton(c.icon.get());
b.setUIID("ThumbIcon");
p.add(b);
b.addActionListener(e -> {
DetailsForm.create(c).show();
});
}
p.setScrollableX(true);
return p;
}
public static HomeForm create(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
HomeForm h = new HomeForm();
h.init(lead, popularTitles, myTitles, recommended);
return h;
}
}
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
I’ll skip to the bottom first. This class is created using a create method that returns the class instance. It accepts the content information as the argument to build the UI.
The actual implementation of the layout is in the init method we see above.
public class HomeForm extends BaseForm {
private HomeForm() {
super(new BorderLayout());
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The init method creates the entire UI. It start with the logo title we can see here. That matches the same image we see in the splash screen. That’s mostly laziness on my
part but isn’t too far off from the actual Netflix UI
public class HomeForm extends BaseForm {
private HomeForm() {
super(new BorderLayout());
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
By adding a command to the side menu the hamburger menu appears automatically. I didn’t want to go into the design and implementation of the side menu so I left this
effectively blank.
public class HomeForm extends BaseForm {
private HomeForm() {
super(new BorderLayout());
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
I also added a search command which is again blank since I didn’t implement that. Technically I just used that for the icon. I could have just used
addMaterialCommandToRightBar but that would have required a slightly longer line of code so I chose this approach
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The main UI has a logo image here which is different from the background hero shot. 

Now you might be thinking: why not have the logo as a part of the background hero shot? Why do we need a separate image for the logo?

Two reasons:

- We want the logo to appear above the play button exactly. If it’s a part of the background image we won’t be able to tell where that is. 

- We want the ability to scale the background and foreground image differently. In the background we want a scale to fill so the UI will look good in all resolutions. For the
foreground we want a scale to fit behavior so the logo text will always be visible regardless of the device resolution.
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
We set the UIID for the series logo. This impacts the following CSS.
ToolbarGradient {
border: none;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6));
padding: 2mm;
}
ThumbIcon {
background: transparent;
margin: 0px;
padding: 8mm;
padding-right: 3mm;
}
HeroImageLogo {
margin: 35mm 7mm 5mm 7mm;
padding: 3.5mm;
background: transparent;
}
HeroImagePlayButton {
border: none;
border-radius: 1.5mm;
background-color: #D8D8D8;
color: black;
font-family: "native:MainRegular";
font-size: 3mm;
padding: 2mm 4mm 2mm 4mm;
}
Lead {
color: white;
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The margin and padding push the logo to the right location in the middle with the right amount of spacing and the background is defined as transparent so the
background image will be visible through the logo.
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The play button looks like this. Again most of the work is done in the CSS for the button
background: transparent;
margin: 0px;
padding: 8mm;
padding-right: 3mm;
}
HeroImageLogo {
margin: 35mm 7mm 5mm 7mm;
padding: 3.5mm;
background: transparent;
}
HeroImagePlayButton {
border: none;
border-radius: 1.5mm;
background-color: #D8D8D8;
color: black;
font-family: "native:MainRegular";
font-size: 3mm;
padding: 2mm 4mm 2mm 4mm;
}
Lead {
color: white;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.01));
padding: 5mm 2mm 0.3mm 2mm;
font-family: "native:MainRegular";
font-size: 2.4mm;
}
PlayNowButton {
color: white;
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
We use a 1.5mm round border with a gray background and black foreground for the text/icons
}
private void init(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
Resources r = Resources.getGlobalResources();
Toolbar tb = getToolbar();
((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png"));
// adding this just so the sidemenu and search icon appear
tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{});
tb.addSearchCommand(e -> {});
ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get());
logo.setUIID("HeroImageLogo");
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The background image comes dynamically from the server so we can’t set it from CSS. We create a box layout with the logo, play button and the “Popular on Netflix”
label. We then set the background image dynamically using the style object.
}
HeroImagePlayButton {
border: none;
border-radius: 1.5mm;
background-color: #D8D8D8;
color: black;
font-family: "native:MainRegular";
font-size: 3mm;
padding: 2mm 4mm 2mm 4mm;
}
Lead {
color: white;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.01));
padding: 5mm 2mm 0.3mm 2mm;
font-family: "native:MainRegular";
font-size: 2.4mm;
}
PlayNowButton {
color: white;
background-color: rgba(0, 0, 0, 0.6);
border: 2px #ffffff cn1-round-border;
padding: 2mm;
font-family: "native:MainRegular";
font-size: 3.5mm;
margin: 15mm 7mm 15mm 7mm;
}
ShowTitleOverlay {
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The Lead UIID is a special case with a dark gradient background. It’s overlaid on the title image and needs that gradient to be visible on all image backgrounds
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
new Label("Recommended", "Lead"),
movieList(recommended));
content.setScrollableY(true);
tabs.addTab("Home", MATERIAL_HOME, 4, content);
tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO"));
tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO"));
tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO"));
add(CENTER, tabs);
}
private Container movieList(List<Content> lst) {
Container p = new Container(BoxLayout.x());
for(Content c : lst) {
ScaleImageButton b = new ScaleImageButton(c.icon.get());
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
The tabs are set to appear at the bottom explicitly to avoid top Android style tabs. I could have defined this in a theme constant but chose to do it in the code in this
case.
padding: 3mm;
margin: 0px;
}
DescriptionFormButton {
color: white;
font-family: "native:MainLight";
font-size: 3.5mm;
padding: 3mm;
margin: 0px;
}
Tab {
color: white;
font-family: "native:MainLight";
font-size: 2.1mm;
border: none;
background-color: black;
padding: 1.5mm 0.5mm 1.5mm 0.5mm;
margin: 0px;
}
/* =============== IMAGES ============= */
ImageImports1 {
background-image: url(images/data/show-logo.png),
url(images/data/hero-background.jpg),
url(images/data/thumb1.jpg),
url(images/data/thumb2.jpg),
url(images/data/thumb3.jpg),
url(images/data/thumb4.jpg),
Source Listing - CSS
codenameone.com github.com/codenameone/CodenameOne
The Lead UIID is a special case with a dark gradient background. It’s overlaid on the title image and needs that gradient to be visible on all image backgrounds
Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton");
Container heroTitle = BoxLayout.encloseY(logo,
FlowLayout.encloseCenter(play),
new Label("Popular on Netflix", "Lead"));
Style stl = heroTitle.getAllStyles();
stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
stl.setBgImage(lead.heroImage.get());
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
new Label("Recommended", "Lead"),
movieList(recommended));
content.setScrollableY(true);
tabs.addTab("Home", MATERIAL_HOME, 4, content);
tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO"));
tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO"));
tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO"));
add(CENTER, tabs);
}
private Container movieList(List<Content> lst) {
Container p = new Container(BoxLayout.x());
for(Content c : lst) {
ScaleImageButton b = new ScaleImageButton(c.icon.get());
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
Each list below is created via the movieList method. They have a lead label top and reside within a scrollable Container so we can scroll through them.
tabs.addTab("Home", MATERIAL_HOME, 4, content);
tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO"));
tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO"));
tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO"));
add(CENTER, tabs);
}
private Container movieList(List<Content> lst) {
Container p = new Container(BoxLayout.x());
for(Content c : lst) {
ScaleImageButton b = new ScaleImageButton(c.icon.get());
b.setUIID("ThumbIcon");
p.add(b);
b.addActionListener(e -> {
DetailsForm.create(c).show();
});
}
p.setScrollableX(true);
return p;
}
public static HomeForm create(Content lead, List<Content> popularTitles,
List<Content> myTitles,
List<Content> recommended) {
HomeForm h = new HomeForm();
h.init(lead, popularTitles, myTitles, recommended);
return h;
}
}
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
Let’s look at the movie list method. Here I create a box X container that’s scrollable on the X axis. Every element is a ScalableImageButton that uses the ThumbIcon UIID.
When pressed we show the DetailsForm.
Tabs tabs = new Tabs();
tabs.setTabPlacement(BOTTOM);
Container content = BoxLayout.encloseY(heroTitle,
movieList(popularTitles),
new Label("My List", "Lead"),
movieList(myTitles),
new Label("Recommended", "Lead"),
movieList(recommended));
content.setScrollableY(true);
tabs.addTab("Home", MATERIAL_HOME, 4, content);
tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO"));
tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO"));
tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO"));
add(CENTER, tabs);
}
private Container movieList(List<Content> lst) {
Container p = new Container(BoxLayout.x());
for(Content c : lst) {
ScaleImageButton b = new ScaleImageButton(c.icon.get());
b.setUIID("ThumbIcon");
p.add(b);
b.addActionListener(e -> {
DetailsForm.create(c).show();
});
}
p.setScrollableX(true);
return p;
Source Listing - HomeForm
codenameone.com github.com/codenameone/CodenameOne
Finally the tabs themselves are added to the bottom of the form.
codenameone.com github.com/codenameone/CodenameOne
Thank You
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
Thank You
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
Thanks for watching I hope you’ll enjoy the rest of the course and find it educational

More Related Content

Similar to create-netflix-clone-06-client-ui_transcript.pdf

Desenvolva um game para android ou iPhone
Desenvolva um game para android ou iPhoneDesenvolva um game para android ou iPhone
Desenvolva um game para android ou iPhoneTiago Oliveira
 
Using prime[31] to connect your unity game to azure mobile services
Using prime[31] to connect your unity game to azure mobile servicesUsing prime[31] to connect your unity game to azure mobile services
Using prime[31] to connect your unity game to azure mobile servicesDavid Voyles
 
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
 
Build Your Own Instagram Filters
Build Your Own Instagram FiltersBuild Your Own Instagram Filters
Build Your Own Instagram FiltersTJ Stalcup
 
Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?ColdFusionConference
 
How to add watermark to image using php
How to add watermark to image using phpHow to add watermark to image using php
How to add watermark to image using phpYourBlogCoach1
 
Getting started with Appcelerator Titanium
Getting started with Appcelerator TitaniumGetting started with Appcelerator Titanium
Getting started with Appcelerator TitaniumTechday7
 
A winning combination: Plone as CMS and your favorite Python web framework as...
A winning combination: Plone as CMS and your favorite Python web framework as...A winning combination: Plone as CMS and your favorite Python web framework as...
A winning combination: Plone as CMS and your favorite Python web framework as...Carlos de la Guardia
 
Getting started with titanium
Getting started with titaniumGetting started with titanium
Getting started with titaniumNaga Harish M
 
A intro to (hosted) Shiny Apps
A intro to (hosted) Shiny AppsA intro to (hosted) Shiny Apps
A intro to (hosted) Shiny AppsDaniel Koller
 
HTML 5 Step By Step - Ebook
HTML 5 Step By Step - EbookHTML 5 Step By Step - Ebook
HTML 5 Step By Step - EbookScottperrone
 
Sass Essentials at Mobile Camp LA
Sass Essentials at Mobile Camp LASass Essentials at Mobile Camp LA
Sass Essentials at Mobile Camp LAJake Johnson
 
AppDevKit for iOS Development
AppDevKit for iOS DevelopmentAppDevKit for iOS Development
AppDevKit for iOS Developmentanistar sung
 

Similar to create-netflix-clone-06-client-ui_transcript.pdf (20)

iOS training (basic)
iOS training (basic)iOS training (basic)
iOS training (basic)
 
Desenvolva um game para android ou iPhone
Desenvolva um game para android ou iPhoneDesenvolva um game para android ou iPhone
Desenvolva um game para android ou iPhone
 
Using prime[31] to connect your unity game to azure mobile services
Using prime[31] to connect your unity game to azure mobile servicesUsing prime[31] to connect your unity game to azure mobile services
Using prime[31] to connect your unity game to azure mobile services
 
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
 
Build Your Own Instagram Filters
Build Your Own Instagram FiltersBuild Your Own Instagram Filters
Build Your Own Instagram Filters
 
Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?Dependency Injection Why is it awesome and Why should I care?
Dependency Injection Why is it awesome and Why should I care?
 
How to add watermark to image using php
How to add watermark to image using phpHow to add watermark to image using php
How to add watermark to image using php
 
Getting started with Appcelerator Titanium
Getting started with Appcelerator TitaniumGetting started with Appcelerator Titanium
Getting started with Appcelerator Titanium
 
A winning combination: Plone as CMS and your favorite Python web framework as...
A winning combination: Plone as CMS and your favorite Python web framework as...A winning combination: Plone as CMS and your favorite Python web framework as...
A winning combination: Plone as CMS and your favorite Python web framework as...
 
Getting started with titanium
Getting started with titaniumGetting started with titanium
Getting started with titanium
 
Supercharge your ui
Supercharge your uiSupercharge your ui
Supercharge your ui
 
Bcsl 031 solve assignment
Bcsl 031 solve assignmentBcsl 031 solve assignment
Bcsl 031 solve assignment
 
A intro to (hosted) Shiny Apps
A intro to (hosted) Shiny AppsA intro to (hosted) Shiny Apps
A intro to (hosted) Shiny Apps
 
Ruby On Rails Starter Kit
Ruby On Rails Starter KitRuby On Rails Starter Kit
Ruby On Rails Starter Kit
 
HTML 5 Step By Step - Ebook
HTML 5 Step By Step - EbookHTML 5 Step By Step - Ebook
HTML 5 Step By Step - Ebook
 
Group111
Group111Group111
Group111
 
Session 1 - c++ intro
Session   1 - c++ introSession   1 - c++ intro
Session 1 - c++ intro
 
Sass Essentials at Mobile Camp LA
Sass Essentials at Mobile Camp LASass Essentials at Mobile Camp LA
Sass Essentials at Mobile Camp LA
 
AppDevKit for iOS Development
AppDevKit for iOS DevelopmentAppDevKit for iOS Development
AppDevKit for iOS Development
 
CAP 756 UNIT 1.pptx
CAP 756 UNIT 1.pptxCAP 756 UNIT 1.pptx
CAP 756 UNIT 1.pptx
 

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-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-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
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.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-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-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
 
Creating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdfCreating a Whatsapp Clone - Part IX.pdf
Creating a Whatsapp Clone - Part IX.pdf
 
Creating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdfCreating a Whatsapp Clone - Part VI.pdf
Creating a Whatsapp Clone - Part VI.pdf
 

Recently uploaded

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
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
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
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
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 

Recently uploaded (20)

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
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
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
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
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 

create-netflix-clone-06-client-ui_transcript.pdf

  • 1. Creating a Netflix Clone VI For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms either so this isn’t too different from the original. The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
  • 2. codenameone.com github.com/codenameone/CodenameOne Client UI For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms either so this isn’t too different from the original. The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
  • 3. codenameone.com github.com/codenameone/CodenameOne Client UI There are just three forms in the demo We’re using layered toolbar for the UI CSS is used for the UI design For this final part we’ll cover the UI of the client which is relatively simple. We don’t have that many forms but to be fair the basic Netflix app doesn’t have too many forms either so this isn’t too different from the original. The one special thing we do here is use the layered toolbar for the UI which I will discuss soon enough CSS is used for the design, I’ll introduce the applicable CSS incrementally when introducing a specific UIID
  • 4. #Constants { includeNativeBool: true; labelGap: 1; } Form { background: black; } TitleCommand { color: white; } ToolbarGradient { border: none; background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6)); padding: 2mm; } ThumbIcon { background: transparent; margin: 0px; padding: 8mm; padding-right: 3mm; } Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne I won’t go over the entire CSS and will instead go back to it when introducing specific UIIDs. I will cover the generic concepts first though. We have only two constants in the CSS. The first is the include native feature which should be on by default always. The second is a standard label gap between the label and an icon. I think 1 millimeter is generally a good number here.
  • 5. public class BaseForm extends Form { public BaseForm(Layout l) { super(l); } @Override protected void initGlobalToolbar() { Toolbar tb = new Toolbar(true); setToolbar(tb); tb.setUIID("ToolbarGradient"); } } Source Listing - BaseForm codenameone.com github.com/codenameone/CodenameOne We don’t have too much in common between the forms at this level of the UI so there’s only one method in common. The initGlobalToolbar method initializes the toolbar component when global toolbar is turned on (which is the default). We do two things here. We set the toolbar to use the layered mode. We do that by passing true to the toolbar constructor. 
 Next we set the UIID of the Toolbar to ToolbarGradient which we use to indicate the translucent gradient background to separate the toolbar from the content
  • 6. } Form { background: black; } TitleCommand { color: white; } ToolbarGradient { border: none; background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6)); padding: 2mm; } ThumbIcon { background: transparent; margin: 0px; padding: 8mm; padding-right: 3mm; } HeroImageLogo { /* margin: 35% 22% 5mm 22%; */ margin: 35mm 7mm 5mm 7mm; padding: 3.5mm; background: transparent; } HeroImagePlayButton { border: none; Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The toolbar gradient is a gradient in black between 0.6 alpha to almost clear alpha. This creates a slight fade effect over the title area so the title will still be visible if the image in the background has the same colors. Codename One doesn’t currently support alpha gradients. As a solution the CSS support generates an image of the gradient during build and uses that
  • 7. public class SplashForm extends Form { private SplashForm() { super(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER)); } private void init() { Resources r = Resources.getGlobalResources(); add(CENTER, new Label(r.getImage("netflix-logo.png"))); } public static SplashForm create() { SplashForm h = new SplashForm(); h.init(); return h; } } Source Listing - SplashForm codenameone.com github.com/codenameone/CodenameOne The splash form is stupid simple. We just place the logo in the center of the form and initialize. There’s only one thing to discuss here and that’s the source of the netflix- logo.png file.
  • 8. font-size: 2.1mm; border: none; background-color: black; padding: 1.5mm 0.5mm 1.5mm 0.5mm; margin: 0px; } /* =============== IMAGES ============= */ ImageImports1 { background-image: url(images/data/show-logo.png), url(images/data/hero-background.jpg), url(images/data/thumb1.jpg), url(images/data/thumb2.jpg), url(images/data/thumb3.jpg), url(images/data/thumb4.jpg), url(images/data/thumb5.jpg), url(images/data/thumb6.jpg), url(images/data/thumb7.jpg), url(images/data/thumb8.jpg); cn1-source-dpi: 0; } ImageImports2 { background-image: url(images/netflix-logo.png); cn1-source-dpi: 320; } Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne All the images are declared in the CSS under two dummy UIIDs specifically ImageImports1 and ImageImports2. This just pulls all the images into the resource file so we can later make use of them.
  • 9. font-size: 2.1mm; border: none; background-color: black; padding: 1.5mm 0.5mm 1.5mm 0.5mm; margin: 0px; } /* =============== IMAGES ============= */ ImageImports1 { background-image: url(images/data/show-logo.png), url(images/data/hero-background.jpg), url(images/data/thumb1.jpg), url(images/data/thumb2.jpg), url(images/data/thumb3.jpg), url(images/data/thumb4.jpg), url(images/data/thumb5.jpg), url(images/data/thumb6.jpg), url(images/data/thumb7.jpg), url(images/data/thumb8.jpg); cn1-source-dpi: 0; } ImageImports2 { background-image: url(images/netflix-logo.png); cn1-source-dpi: 320; } Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The source DPI is the reason we have to image import UIIDs. When source DPI is set to 0 it means we want the image imported “as is”. Not as a multi-image but rather as a single image.
  • 10. font-size: 2.1mm; border: none; background-color: black; padding: 1.5mm 0.5mm 1.5mm 0.5mm; margin: 0px; } /* =============== IMAGES ============= */ ImageImports1 { background-image: url(images/data/show-logo.png), url(images/data/hero-background.jpg), url(images/data/thumb1.jpg), url(images/data/thumb2.jpg), url(images/data/thumb3.jpg), url(images/data/thumb4.jpg), url(images/data/thumb5.jpg), url(images/data/thumb6.jpg), url(images/data/thumb7.jpg), url(images/data/thumb8.jpg); cn1-source-dpi: 0; } ImageImports2 { background-image: url(images/netflix-logo.png); cn1-source-dpi: 320; } Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The logo is the only image where we’re interested in multi-image behavior. Since most images come from the server we don’t need many multi-images in the app.
  • 11. public class HomeForm extends BaseForm { private HomeForm() { super(new BorderLayout()); } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The HomeForm is the main UI of the application listing the content that the user can select from. It’s a base form which means the title of the form is overlaid on the content and it uses border layout.
  • 12. add(CENTER, tabs); } private Container movieList(List<Content> lst) { Container p = new Container(BoxLayout.x()); for(Content c : lst) { ScaleImageButton b = new ScaleImageButton(c.icon.get()); b.setUIID("ThumbIcon"); p.add(b); b.addActionListener(e -> { DetailsForm.create(c).show(); }); } p.setScrollableX(true); return p; } public static HomeForm create(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { HomeForm h = new HomeForm(); h.init(lead, popularTitles, myTitles, recommended); return h; } } Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne I’ll skip to the bottom first. This class is created using a create method that returns the class instance. It accepts the content information as the argument to build the UI. The actual implementation of the layout is in the init method we see above.
  • 13. public class HomeForm extends BaseForm { private HomeForm() { super(new BorderLayout()); } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The init method creates the entire UI. It start with the logo title we can see here. That matches the same image we see in the splash screen. That’s mostly laziness on my part but isn’t too far off from the actual Netflix UI
  • 14. public class HomeForm extends BaseForm { private HomeForm() { super(new BorderLayout()); } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne By adding a command to the side menu the hamburger menu appears automatically. I didn’t want to go into the design and implementation of the side menu so I left this effectively blank.
  • 15. public class HomeForm extends BaseForm { private HomeForm() { super(new BorderLayout()); } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne I also added a search command which is again blank since I didn’t implement that. Technically I just used that for the icon. I could have just used addMaterialCommandToRightBar but that would have required a slightly longer line of code so I chose this approach
  • 16. } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The main UI has a logo image here which is different from the background hero shot. Now you might be thinking: why not have the logo as a part of the background hero shot? Why do we need a separate image for the logo?
 Two reasons:
 - We want the logo to appear above the play button exactly. If it’s a part of the background image we won’t be able to tell where that is. - We want the ability to scale the background and foreground image differently. In the background we want a scale to fill so the UI will look good in all resolutions. For the foreground we want a scale to fit behavior so the logo text will always be visible regardless of the device resolution.
  • 17. } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne We set the UIID for the series logo. This impacts the following CSS.
  • 18. ToolbarGradient { border: none; background: linear-gradient(0deg, rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.6)); padding: 2mm; } ThumbIcon { background: transparent; margin: 0px; padding: 8mm; padding-right: 3mm; } HeroImageLogo { margin: 35mm 7mm 5mm 7mm; padding: 3.5mm; background: transparent; } HeroImagePlayButton { border: none; border-radius: 1.5mm; background-color: #D8D8D8; color: black; font-family: "native:MainRegular"; font-size: 3mm; padding: 2mm 4mm 2mm 4mm; } Lead { color: white; Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The margin and padding push the logo to the right location in the middle with the right amount of spacing and the background is defined as transparent so the background image will be visible through the logo.
  • 19. } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The play button looks like this. Again most of the work is done in the CSS for the button
  • 20. background: transparent; margin: 0px; padding: 8mm; padding-right: 3mm; } HeroImageLogo { margin: 35mm 7mm 5mm 7mm; padding: 3.5mm; background: transparent; } HeroImagePlayButton { border: none; border-radius: 1.5mm; background-color: #D8D8D8; color: black; font-family: "native:MainRegular"; font-size: 3mm; padding: 2mm 4mm 2mm 4mm; } Lead { color: white; background: linear-gradient(0deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.01)); padding: 5mm 2mm 0.3mm 2mm; font-family: "native:MainRegular"; font-size: 2.4mm; } PlayNowButton { color: white; Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne We use a 1.5mm round border with a gray background and black foreground for the text/icons
  • 21. } private void init(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { Resources r = Resources.getGlobalResources(); Toolbar tb = getToolbar(); ((Label)tb.getTitleComponent()).setIcon(r.getImage("netflix-logo.png")); // adding this just so the sidemenu and search icon appear tb.addMaterialCommandToLeftSideMenu("Placeholder", MATERIAL_AIRPLANEMODE_ON, e ->{}); tb.addSearchCommand(e -> {}); ScaleImageLabel logo = new ScaleImageLabel(lead.logo.get()); logo.setUIID("HeroImageLogo"); Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The background image comes dynamically from the server so we can’t set it from CSS. We create a box layout with the logo, play button and the “Popular on Netflix” label. We then set the background image dynamically using the style object.
  • 22. } HeroImagePlayButton { border: none; border-radius: 1.5mm; background-color: #D8D8D8; color: black; font-family: "native:MainRegular"; font-size: 3mm; padding: 2mm 4mm 2mm 4mm; } Lead { color: white; background: linear-gradient(0deg, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.01)); padding: 5mm 2mm 0.3mm 2mm; font-family: "native:MainRegular"; font-size: 2.4mm; } PlayNowButton { color: white; background-color: rgba(0, 0, 0, 0.6); border: 2px #ffffff cn1-round-border; padding: 2mm; font-family: "native:MainRegular"; font-size: 3.5mm; margin: 15mm 7mm 15mm 7mm; } ShowTitleOverlay { Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The Lead UIID is a special case with a dark gradient background. It’s overlaid on the title image and needs that gradient to be visible on all image backgrounds
  • 23. Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), new Label("Recommended", "Lead"), movieList(recommended)); content.setScrollableY(true); tabs.addTab("Home", MATERIAL_HOME, 4, content); tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO")); tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO")); tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO")); add(CENTER, tabs); } private Container movieList(List<Content> lst) { Container p = new Container(BoxLayout.x()); for(Content c : lst) { ScaleImageButton b = new ScaleImageButton(c.icon.get()); Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne The tabs are set to appear at the bottom explicitly to avoid top Android style tabs. I could have defined this in a theme constant but chose to do it in the code in this case.
  • 24. padding: 3mm; margin: 0px; } DescriptionFormButton { color: white; font-family: "native:MainLight"; font-size: 3.5mm; padding: 3mm; margin: 0px; } Tab { color: white; font-family: "native:MainLight"; font-size: 2.1mm; border: none; background-color: black; padding: 1.5mm 0.5mm 1.5mm 0.5mm; margin: 0px; } /* =============== IMAGES ============= */ ImageImports1 { background-image: url(images/data/show-logo.png), url(images/data/hero-background.jpg), url(images/data/thumb1.jpg), url(images/data/thumb2.jpg), url(images/data/thumb3.jpg), url(images/data/thumb4.jpg), Source Listing - CSS codenameone.com github.com/codenameone/CodenameOne The Lead UIID is a special case with a dark gradient background. It’s overlaid on the title image and needs that gradient to be visible on all image backgrounds
  • 25. Button play = new Button("PLAY", MATERIAL_PLAY_ARROW, "HeroImagePlayButton"); Container heroTitle = BoxLayout.encloseY(logo, FlowLayout.encloseCenter(play), new Label("Popular on Netflix", "Lead")); Style stl = heroTitle.getAllStyles(); stl.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL); stl.setBgImage(lead.heroImage.get()); Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), new Label("Recommended", "Lead"), movieList(recommended)); content.setScrollableY(true); tabs.addTab("Home", MATERIAL_HOME, 4, content); tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO")); tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO")); tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO")); add(CENTER, tabs); } private Container movieList(List<Content> lst) { Container p = new Container(BoxLayout.x()); for(Content c : lst) { ScaleImageButton b = new ScaleImageButton(c.icon.get()); Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne Each list below is created via the movieList method. They have a lead label top and reside within a scrollable Container so we can scroll through them.
  • 26. tabs.addTab("Home", MATERIAL_HOME, 4, content); tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO")); tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO")); tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO")); add(CENTER, tabs); } private Container movieList(List<Content> lst) { Container p = new Container(BoxLayout.x()); for(Content c : lst) { ScaleImageButton b = new ScaleImageButton(c.icon.get()); b.setUIID("ThumbIcon"); p.add(b); b.addActionListener(e -> { DetailsForm.create(c).show(); }); } p.setScrollableX(true); return p; } public static HomeForm create(Content lead, List<Content> popularTitles, List<Content> myTitles, List<Content> recommended) { HomeForm h = new HomeForm(); h.init(lead, popularTitles, myTitles, recommended); return h; } } Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne Let’s look at the movie list method. Here I create a box X container that’s scrollable on the X axis. Every element is a ScalableImageButton that uses the ThumbIcon UIID. When pressed we show the DetailsForm.
  • 27. Tabs tabs = new Tabs(); tabs.setTabPlacement(BOTTOM); Container content = BoxLayout.encloseY(heroTitle, movieList(popularTitles), new Label("My List", "Lead"), movieList(myTitles), new Label("Recommended", "Lead"), movieList(recommended)); content.setScrollableY(true); tabs.addTab("Home", MATERIAL_HOME, 4, content); tabs.addTab("Search", MATERIAL_SEARCH, 4, new Label("TODO")); tabs.addTab("Coming Soon", MATERIAL_ONDEMAND_VIDEO, 4, new Label("TODO")); tabs.addTab("More", MATERIAL_MENU, 4, new Label("TODO")); add(CENTER, tabs); } private Container movieList(List<Content> lst) { Container p = new Container(BoxLayout.x()); for(Content c : lst) { ScaleImageButton b = new ScaleImageButton(c.icon.get()); b.setUIID("ThumbIcon"); p.add(b); b.addActionListener(e -> { DetailsForm.create(c).show(); }); } p.setScrollableX(true); return p; Source Listing - HomeForm codenameone.com github.com/codenameone/CodenameOne Finally the tabs themselves are added to the bottom of the form.
  • 28. codenameone.com github.com/codenameone/CodenameOne Thank You Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
  • 29. Thank You Thanks for watching I hope you’ll enjoy the rest of the course and find it educational
  • 30. Thanks for watching I hope you’ll enjoy the rest of the course and find it educational