SlideShare a Scribd company logo
Creating a Facebook Clone - Part XII
We are now ready to implement the newsfeed
© Codename One 2017 all rights reserved
The NewsfeedContainer class is a bit big so I split down the functionality into smaller methods that build the various pieces of the UI. We'll use InfiniteContainer which
lets us scroll an infinite list of entries and implements pull-to-refresh.
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
Now that we have a sense of what we are building lets look at the high level class skeleton then fill in the pieces.

We use the last time value when invoking fetchTimelinePosts so the latest data arrives
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
This method is invoked when the user scrolls and needs more data or when pull-to-refresh is used
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
It's hard to tell how many components we'll return so we'll use a list and convert it to an array when we are done
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
If this is the first entry we reset the value of lastTime before making the request
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
The top is always the same component: the post bar. That's where we can add a new post
public class NewsfeedContainer extends InfiniteContainer {
private long lastTime;
@Override
public Component[] fetchComponents(int index, int amount) {
List<Component> components = new ArrayList<>();
if(index == 0) {
lastTime = System.currentTimeMillis();
components.add(createPostBar());
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
NewsfeedContainer
We check with the server if there are more posts, if we reached the end null is returned and we return that too
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
NewsfeedContainer
Otherwise we loop over the responses and create a news item for each post, we add it with a space separator. Notice the usage of createNewsItem() in the code
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
NewsfeedContainer
We update the lastTime value to the last element so the next request continues from the last place
components.add(UIUtils.createSpace());
}
List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount);
if(response == null) {
return null;
}
for(Post p : response) {
components.add(createNewsItem(p.user.get(), p));
components.add(UIUtils.createHalfSpace());
lastTime = p.date.getLong();
}
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
NewsfeedContainer
We convert the list to an array and return that data
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
NewsfeedContainer
We referenced createPostBar() & createNewsItem() lets look at the former first, it's a relatively simple method.
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
NewsfeedContainer
There are 3 buttons here none of which look like a button & each one should take us to a different Form in the finished app
Component[] cmps = new Component[components.size()];
components.toArray(cmps);
return cmps;
}
private Container createPostBar() {
Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label");
Button writePost = new Button("What's on your mind?",
"NewPostButton");
Button gallery = new Button("Photo", "GalleryButton");
FontImage.setMaterialIcon(gallery,
FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f);
gallery.setTextPosition(BOTTOM);
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
NewsfeedContainer
We lay out the entire title area in a border layout, this allows the writePost field to grow and occupy the available space
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
NewsfeedContainer
Next we have the createNewsTitle() method which is very similar in some regards
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
NewsfeedContainer
We need to format the time as something that's more human readable such as "3 minutes ago"
Container c = BorderLayout.centerEastWest(writePost, gallery, avatar);
c.setUIID("HalfPaddedContainer");
return c;
}
private Container createNewsTitle(User u, Post p) {
Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton");
Button name = new Button(u.fullName(), "PostTitle");
Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()),
"PostSubTitle");
Button menu = new Button("", "Label");
FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ);
Container titleArea = BorderLayout.centerEastWest(
FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)),
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
NewsfeedContainer
Again we place the elements in a border layout but this time we wrap them in flow layouts within so they vertically align properly. This is subtle and hard to notice in the
image. Notice that the name jots up just a little bit in the unaligned version but this can be more obvious when the font is smaller or the icon is larger. Also notice the
placement of the menu button.
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
if(p.comments.size() > 0) {
stats.add(EAST,new Label(p.comments.size() +
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
NewsfeedContainer
Next up is createPostStats it's again a very simple method. I broke it out just to keep the code size smaller so we can review the blocks more easily.
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
if(p.comments.size() > 0) {
stats.add(EAST,new Label(p.comments.size() +
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
NewsfeedContainer
The stats bar appears above the like button. It's only there if there are likes or comments otherwise it shouldn't show.
FlowLayout.encloseIn(menu), avatar);
titleArea.setUIID("HalfPaddedContainer");
return titleArea;
}
private Container createPostStats(Post p) {
Container stats=new Container(new BorderLayout(), "PaddedContainer");
if(p.likes.size() > 0) {
Label thumbUp = new Label("", "SmallBlueCircle");
FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP);
Label count = new Label("" + p.likes.size(), "SmallLabel");
stats.add(WEST, BoxLayout.encloseX(thumbUp, count));
}
if(p.comments.size() > 0) {
stats.add(EAST,new Label(p.comments.size() +
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
NewsfeedContainer
I could have made this code better so it will write "1 comment" and "2 comments" etc. but I wanted to keep the code simple
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
body.setUIID("HalfPaddedContainer");
Button like = new Button("Like", "CleanButton");
Button comment = new Button("Comment", "CleanButton");
Button share = new Button("Share", "CleanButton");
FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP);
FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT);
FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE);
Container buttonBar = GridLayout.encloseIn(3, like, comment, share);
buttonBar.setUIID("HalfPaddedContainer");
return BoxLayout.encloseY(titleArea, body,
createPostStats(p), buttonBar);
}
}
NewsfeedContainer
With that we are ready for the final method in this class createNewsItem
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
body.setUIID("HalfPaddedContainer");
Button like = new Button("Like", "CleanButton");
Button comment = new Button("Comment", "CleanButton");
Button share = new Button("Share", "CleanButton");
FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP);
FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT);
FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE);
Container buttonBar = GridLayout.encloseIn(3, like, comment, share);
buttonBar.setUIID("HalfPaddedContainer");
return BoxLayout.encloseY(titleArea, body,
createPostStats(p), buttonBar);
}
}
NewsfeedContainer
This method assembles the post entry from the pieces we already built, the title entry. The rich text view or span label etc.
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
body.setUIID("HalfPaddedContainer");
Button like = new Button("Like", "CleanButton");
Button comment = new Button("Comment", "CleanButton");
Button share = new Button("Share", "CleanButton");
FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP);
FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT);
FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE);
Container buttonBar = GridLayout.encloseIn(3, like, comment, share);
buttonBar.setUIID("HalfPaddedContainer");
return BoxLayout.encloseY(titleArea, body,
createPostStats(p), buttonBar);
}
}
NewsfeedContainer
The three like/comment/share buttons are packaged in a grid so they will have the same size and align properly
" comments","SmallLabel"));
}
return stats;
}
private Container createNewsItem(User u, Post p) {
Container titleArea = createNewsTitle(u, p);
Component body;
if(p.content.get().indexOf('<') > -1) {
body = new RichTextView(p.content.get());
} else {
body = new SpanLabel(p.content.get());
}
body.setUIID("HalfPaddedContainer");
Button like = new Button("Like", "CleanButton");
Button comment = new Button("Comment", "CleanButton");
Button share = new Button("Share", "CleanButton");
FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP);
FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT);
FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE);
Container buttonBar = GridLayout.encloseIn(3, like, comment, share);
buttonBar.setUIID("HalfPaddedContainer");
return BoxLayout.encloseY(titleArea, body,
createPostStats(p), buttonBar);
}
}
NewsfeedContainer
Everything else is packaged together on a Y axis Container with good padding
© Codename One 2017 all rights reserved
We have quite a few UIID’s we didn’t cover in CSS yet so lets go into those…
NewPostButton {
border: 1px #cccccc cn1-pill-border;
background-color: white;
padding: 1mm 2mm 1mm 2mm;
margin: 1mm;
color: black;
font-family: "native:MainLight";
font-size: 2.6mm;
}
GalleryButton {
color: #4B4F56;
font-family: "native:MainLight";
font-size: 1.5mm;
text-align: center;
padding: 0px 2mm 0px 2mm;
margin: 0px;
}
HalfPaddedContainer {
padding: 1.5mm;
margin: 0mm;
background: transparent;
}
theme.css
The button looks like a text field, when we press it we go directly to the post editing form
NewPostButton {
border: 1px #cccccc cn1-pill-border;
background-color: white;
padding: 1mm 2mm 1mm 2mm;
margin: 1mm;
color: black;
font-family: "native:MainLight";
font-size: 2.6mm;
}
GalleryButton {
color: #4B4F56;
font-family: "native:MainLight";
font-size: 1.5mm;
text-align: center;
padding: 0px 2mm 0px 2mm;
margin: 0px;
}
HalfPaddedContainer {
padding: 1.5mm;
margin: 0mm;
background: transparent;
}
theme.css
The gallery button has small font and padding so it can fit in the same row as the other elements
color: #4B4F56;
font-family: "native:MainLight";
font-size: 1.5mm;
text-align: center;
padding: 0px 2mm 0px 2mm;
margin: 0px;
}
HalfPaddedContainer {
padding: 1.5mm;
margin: 0mm;
background: transparent;
}
CleanButton {
cn1-derive: BaseButton;
font-family: "native:MainLight";
font-size: 2.5mm;
margin: 0px;
color: #4B4F56;
}
PostTitle {
font-family: "native:MainRegular";
font-size: 3.5mm;
}
PostSubTitle {
font-family: "native:MainRegular";
theme.css
The PaddedContainer UIID is often too heavily padded for some UI elements, this uses 1.5mm instead of 3mm in the standard padding
color: #4B4F56;
font-family: "native:MainLight";
font-size: 1.5mm;
text-align: center;
padding: 0px 2mm 0px 2mm;
margin: 0px;
}
HalfPaddedContainer {
padding: 1.5mm;
margin: 0mm;
background: transparent;
}
CleanButton {
cn1-derive: BaseButton;
font-family: "native:MainLight";
font-size: 2.5mm;
margin: 0px;
color: #4B4F56;
}
PostTitle {
font-family: "native:MainRegular";
font-size: 3.5mm;
}
PostSubTitle {
font-family: "native:MainRegular";
theme.css
We'll use this in several places as we go on, here it doesn't matter as much as it mostly shows an icon
cn1-derive: BaseButton;
font-family: "native:MainLight";
font-size: 2.5mm;
margin: 0px;
color: #4B4F56;
}
PostTitle {
font-family: "native:MainRegular";
font-size: 3.5mm;
}
PostSubTitle {
font-family: "native:MainRegular";
font-size: 2mm;
color: #aaaaaa;
}
SmallBlueCircle {
border: cn1-round-border;
background-color: blue;
padding: 0.5mm;
margin: 0px 2mm 0px 1mm;
color: white;
font-family: "native:MainLight";
font-size: 2mm;
}
SmallLabel {
theme.css
The title and subtitle use regular font which makes them standout next to the light font used in the rest of the app
color: #4B4F56;
}
PostTitle {
font-family: "native:MainRegular";
font-size: 3.5mm;
}
PostSubTitle {
font-family: "native:MainRegular";
font-size: 2mm;
color: #aaaaaa;
}
SmallBlueCircle {
border: cn1-round-border;
background-color: blue;
padding: 0.5mm;
margin: 0px 2mm 0px 1mm;
color: white;
font-family: "native:MainLight";
font-size: 2mm;
}
SmallLabel {
font-family: "native:MainLight";
font-size: 2mm;
}
theme.css
This is literally a round blue circle with enough padding to show the thumb within the circle
color: #4B4F56;
}
PostTitle {
font-family: "native:MainRegular";
font-size: 3.5mm;
}
PostSubTitle {
font-family: "native:MainRegular";
font-size: 2mm;
color: #aaaaaa;
}
SmallBlueCircle {
border: cn1-round-border;
background-color: blue;
padding: 0.5mm;
margin: 0px 2mm 0px 1mm;
color: white;
font-family: "native:MainLight";
font-size: 2mm;
}
SmallLabel {
font-family: "native:MainLight";
font-size: 2mm;
}
theme.css
I just need a label with a font small enough to fit. That's all the CSS entries we need so we can lean back, press play in the IDE and watch the first form element in the
application come to life!

More Related Content

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

React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
Soluto
 
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfCreating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
ShaiAlmog1
 
Higher Order Components and Render Props
Higher Order Components and Render PropsHigher Order Components and Render Props
Higher Order Components and Render Props
Nitish Phanse
 
Day 5
Day 5Day 5
Creating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdfCreating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdf
ShaiAlmog1
 
Send Sms with SmsManager Api In Android with Kotlin
Send Sms with SmsManager Api In Android with KotlinSend Sms with SmsManager Api In Android with Kotlin
Send Sms with SmsManager Api In Android with Kotlin
ShahRushika
 
Creating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdfCreating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdf
ShaiAlmog1
 
Creating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdfCreating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdf
ShaiAlmog1
 
Creating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdfCreating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdf
ShaiAlmog1
 
Aspect-Oriented Programming (AOP) in .NET
Aspect-Oriented Programming (AOP) in .NETAspect-Oriented Programming (AOP) in .NET
Aspect-Oriented Programming (AOP) in .NET
Yuriy Guts
 
C++ Please I am posting the fifth time and hoping to get th.pdf
C++ Please I am posting the fifth time and hoping to get th.pdfC++ Please I am posting the fifth time and hoping to get th.pdf
C++ Please I am posting the fifth time and hoping to get th.pdf
jaipur2
 
Functional programming 101
Functional programming 101Functional programming 101
Functional programming 101
Maneesh Chaturvedi
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Codemotion
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Mario Fusco
 
JAVA.Q4 Create a Time class. This class will represent a point in.pdf
JAVA.Q4 Create a Time class. This class will represent a point in.pdfJAVA.Q4 Create a Time class. This class will represent a point in.pdf
JAVA.Q4 Create a Time class. This class will represent a point in.pdf
karymadelaneyrenne19
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter Client
Kenji Tanaka
 
An object of class StatCalc can be used to compute several simp.pdf
 An object of class StatCalc can be used to compute several simp.pdf An object of class StatCalc can be used to compute several simp.pdf
An object of class StatCalc can be used to compute several simp.pdf
aravlitraders2012
 
GWT Training - Session 3/3
GWT Training - Session 3/3GWT Training - Session 3/3
GWT Training - Session 3/3
Faiz Bashir
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy codeShriKant Vashishtha
 

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

React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
 
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfCreating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
 
Higher Order Components and Render Props
Higher Order Components and Render PropsHigher Order Components and Render Props
Higher Order Components and Render Props
 
Day 5
Day 5Day 5
Day 5
 
Creating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdfCreating a Facebook Clone - Part XI.pdf
Creating a Facebook Clone - Part XI.pdf
 
Send Sms with SmsManager Api In Android with Kotlin
Send Sms with SmsManager Api In Android with KotlinSend Sms with SmsManager Api In Android with Kotlin
Send Sms with SmsManager Api In Android with Kotlin
 
Creating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdfCreating a Facebook Clone - Part XL - Transcript.pdf
Creating a Facebook Clone - Part XL - Transcript.pdf
 
C# labprograms
C# labprogramsC# labprograms
C# labprograms
 
Creating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdfCreating an Uber Clone - Part XXXX.pdf
Creating an Uber Clone - Part XXXX.pdf
 
Creating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdfCreating a Facebook Clone - Part XIV.pdf
Creating a Facebook Clone - Part XIV.pdf
 
Aspect-Oriented Programming (AOP) in .NET
Aspect-Oriented Programming (AOP) in .NETAspect-Oriented Programming (AOP) in .NET
Aspect-Oriented Programming (AOP) in .NET
 
C++ Please I am posting the fifth time and hoping to get th.pdf
C++ Please I am posting the fifth time and hoping to get th.pdfC++ Please I am posting the fifth time and hoping to get th.pdf
C++ Please I am posting the fifth time and hoping to get th.pdf
 
Functional programming 101
Functional programming 101Functional programming 101
Functional programming 101
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
JAVA.Q4 Create a Time class. This class will represent a point in.pdf
JAVA.Q4 Create a Time class. This class will represent a point in.pdfJAVA.Q4 Create a Time class. This class will represent a point in.pdf
JAVA.Q4 Create a Time class. This class will represent a point in.pdf
 
Blending Culture in Twitter Client
Blending Culture in Twitter ClientBlending Culture in Twitter Client
Blending Culture in Twitter Client
 
An object of class StatCalc can be used to compute several simp.pdf
 An object of class StatCalc can be used to compute several simp.pdf An object of class StatCalc can be used to compute several simp.pdf
An object of class StatCalc can be used to compute several simp.pdf
 
GWT Training - Session 3/3
GWT Training - Session 3/3GWT Training - Session 3/3
GWT Training - Session 3/3
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 

More from ShaiAlmog1

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

More from ShaiAlmog1 (20)

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

Recently uploaded

"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
Fwdays
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
КАТЕРИНА АБЗЯТОВА «Ефективне планування тестування ключові аспекти та практ...
КАТЕРИНА АБЗЯТОВА  «Ефективне планування тестування  ключові аспекти та практ...КАТЕРИНА АБЗЯТОВА  «Ефективне планування тестування  ключові аспекти та практ...
КАТЕРИНА АБЗЯТОВА «Ефективне планування тестування ключові аспекти та практ...
QADay
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
Elena Simperl
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance
 
UiPath New York Community Day in-person event
UiPath New York Community Day in-person eventUiPath New York Community Day in-person event
UiPath New York Community Day in-person event
DianaGray10
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Tobias Schneck
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
Vlad Stirbu
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 

Recently uploaded (20)

"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
КАТЕРИНА АБЗЯТОВА «Ефективне планування тестування ключові аспекти та практ...
КАТЕРИНА АБЗЯТОВА  «Ефективне планування тестування  ключові аспекти та практ...КАТЕРИНА АБЗЯТОВА  «Ефективне планування тестування  ключові аспекти та практ...
КАТЕРИНА АБЗЯТОВА «Ефективне планування тестування ключові аспекти та практ...
 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Knowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and backKnowledge engineering: from people to machines and back
Knowledge engineering: from people to machines and back
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdfFIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
FIDO Alliance Osaka Seminar: Passkeys at Amazon.pdf
 
UiPath New York Community Day in-person event
UiPath New York Community Day in-person eventUiPath New York Community Day in-person event
UiPath New York Community Day in-person event
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
Kubernetes & AI - Beauty and the Beast !?! @KCD Istanbul 2024
 
Quantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIsQuantum Computing: Current Landscape and the Future Role of APIs
Quantum Computing: Current Landscape and the Future Role of APIs
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 

Creating a Facebook Clone - Part XII - Transcript.pdf

  • 1. Creating a Facebook Clone - Part XII We are now ready to implement the newsfeed
  • 2. © Codename One 2017 all rights reserved The NewsfeedContainer class is a bit big so I split down the functionality into smaller methods that build the various pieces of the UI. We'll use InfiniteContainer which lets us scroll an infinite list of entries and implements pull-to-refresh.
  • 3. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer Now that we have a sense of what we are building lets look at the high level class skeleton then fill in the pieces. We use the last time value when invoking fetchTimelinePosts so the latest data arrives
  • 4. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer This method is invoked when the user scrolls and needs more data or when pull-to-refresh is used
  • 5. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer It's hard to tell how many components we'll return so we'll use a list and convert it to an array when we are done
  • 6. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer If this is the first entry we reset the value of lastTime before making the request
  • 7. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer The top is always the same component: the post bar. That's where we can add a new post
  • 8. public class NewsfeedContainer extends InfiniteContainer { private long lastTime; @Override public Component[] fetchComponents(int index, int amount) { List<Component> components = new ArrayList<>(); if(index == 0) { lastTime = System.currentTimeMillis(); components.add(createPostBar()); components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); NewsfeedContainer We check with the server if there are more posts, if we reached the end null is returned and we return that too
  • 9. components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); NewsfeedContainer Otherwise we loop over the responses and create a news item for each post, we add it with a space separator. Notice the usage of createNewsItem() in the code
  • 10. components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); NewsfeedContainer We update the lastTime value to the last element so the next request continues from the last place
  • 11. components.add(UIUtils.createSpace()); } List<Post> response = ServerAPI.fetchTimelinePosts(lastTime, amount); if(response == null) { return null; } for(Post p : response) { components.add(createNewsItem(p.user.get(), p)); components.add(UIUtils.createHalfSpace()); lastTime = p.date.getLong(); } Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); NewsfeedContainer We convert the list to an array and return that data
  • 12. Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), NewsfeedContainer We referenced createPostBar() & createNewsItem() lets look at the former first, it's a relatively simple method.
  • 13. Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), NewsfeedContainer There are 3 buttons here none of which look like a button & each one should take us to a different Form in the finished app
  • 14. Component[] cmps = new Component[components.size()]; components.toArray(cmps); return cmps; } private Container createPostBar() { Button avatar = new Button(ServerAPI.me().getAvatar(6.5f), "Label"); Button writePost = new Button("What's on your mind?", "NewPostButton"); Button gallery = new Button("Photo", "GalleryButton"); FontImage.setMaterialIcon(gallery, FontImage.MATERIAL_PHOTO_LIBRARY, 2.9f); gallery.setTextPosition(BOTTOM); Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), NewsfeedContainer We lay out the entire title area in a border layout, this allows the writePost field to grow and occupy the available space
  • 15. Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } NewsfeedContainer Next we have the createNewsTitle() method which is very similar in some regards
  • 16. Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } NewsfeedContainer We need to format the time as something that's more human readable such as "3 minutes ago"
  • 17. Container c = BorderLayout.centerEastWest(writePost, gallery, avatar); c.setUIID("HalfPaddedContainer"); return c; } private Container createNewsTitle(User u, Post p) { Button avatar = new Button("", u.getAvatar(6.5f), "CleanButton"); Button name = new Button(u.fullName(), "PostTitle"); Button postTime = new Button(UIUtils.formatTimeAgo(p.date.get()), "PostSubTitle"); Button menu = new Button("", "Label"); FontImage.setMaterialIcon(menu, FontImage.MATERIAL_MORE_HORIZ); Container titleArea = BorderLayout.centerEastWest( FlowLayout.encloseMiddle(BoxLayout.encloseY(name, postTime)), FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } NewsfeedContainer Again we place the elements in a border layout but this time we wrap them in flow layouts within so they vertically align properly. This is subtle and hard to notice in the image. Notice that the name jots up just a little bit in the unaligned version but this can be more obvious when the font is smaller or the icon is larger. Also notice the placement of the menu button.
  • 18. FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } if(p.comments.size() > 0) { stats.add(EAST,new Label(p.comments.size() + " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } NewsfeedContainer Next up is createPostStats it's again a very simple method. I broke it out just to keep the code size smaller so we can review the blocks more easily.
  • 19. FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } if(p.comments.size() > 0) { stats.add(EAST,new Label(p.comments.size() + " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } NewsfeedContainer The stats bar appears above the like button. It's only there if there are likes or comments otherwise it shouldn't show.
  • 20. FlowLayout.encloseIn(menu), avatar); titleArea.setUIID("HalfPaddedContainer"); return titleArea; } private Container createPostStats(Post p) { Container stats=new Container(new BorderLayout(), "PaddedContainer"); if(p.likes.size() > 0) { Label thumbUp = new Label("", "SmallBlueCircle"); FontImage.setMaterialIcon(thumbUp, FontImage.MATERIAL_THUMB_UP); Label count = new Label("" + p.likes.size(), "SmallLabel"); stats.add(WEST, BoxLayout.encloseX(thumbUp, count)); } if(p.comments.size() > 0) { stats.add(EAST,new Label(p.comments.size() + " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } NewsfeedContainer I could have made this code better so it will write "1 comment" and "2 comments" etc. but I wanted to keep the code simple
  • 21. " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } body.setUIID("HalfPaddedContainer"); Button like = new Button("Like", "CleanButton"); Button comment = new Button("Comment", "CleanButton"); Button share = new Button("Share", "CleanButton"); FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP); FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT); FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE); Container buttonBar = GridLayout.encloseIn(3, like, comment, share); buttonBar.setUIID("HalfPaddedContainer"); return BoxLayout.encloseY(titleArea, body, createPostStats(p), buttonBar); } } NewsfeedContainer With that we are ready for the final method in this class createNewsItem
  • 22. " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } body.setUIID("HalfPaddedContainer"); Button like = new Button("Like", "CleanButton"); Button comment = new Button("Comment", "CleanButton"); Button share = new Button("Share", "CleanButton"); FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP); FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT); FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE); Container buttonBar = GridLayout.encloseIn(3, like, comment, share); buttonBar.setUIID("HalfPaddedContainer"); return BoxLayout.encloseY(titleArea, body, createPostStats(p), buttonBar); } } NewsfeedContainer This method assembles the post entry from the pieces we already built, the title entry. The rich text view or span label etc.
  • 23. " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } body.setUIID("HalfPaddedContainer"); Button like = new Button("Like", "CleanButton"); Button comment = new Button("Comment", "CleanButton"); Button share = new Button("Share", "CleanButton"); FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP); FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT); FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE); Container buttonBar = GridLayout.encloseIn(3, like, comment, share); buttonBar.setUIID("HalfPaddedContainer"); return BoxLayout.encloseY(titleArea, body, createPostStats(p), buttonBar); } } NewsfeedContainer The three like/comment/share buttons are packaged in a grid so they will have the same size and align properly
  • 24. " comments","SmallLabel")); } return stats; } private Container createNewsItem(User u, Post p) { Container titleArea = createNewsTitle(u, p); Component body; if(p.content.get().indexOf('<') > -1) { body = new RichTextView(p.content.get()); } else { body = new SpanLabel(p.content.get()); } body.setUIID("HalfPaddedContainer"); Button like = new Button("Like", "CleanButton"); Button comment = new Button("Comment", "CleanButton"); Button share = new Button("Share", "CleanButton"); FontImage.setMaterialIcon(like, FontImage.MATERIAL_THUMB_UP); FontImage.setMaterialIcon(comment, FontImage.MATERIAL_COMMENT); FontImage.setMaterialIcon(share, FontImage.MATERIAL_SHARE); Container buttonBar = GridLayout.encloseIn(3, like, comment, share); buttonBar.setUIID("HalfPaddedContainer"); return BoxLayout.encloseY(titleArea, body, createPostStats(p), buttonBar); } } NewsfeedContainer Everything else is packaged together on a Y axis Container with good padding
  • 25. © Codename One 2017 all rights reserved We have quite a few UIID’s we didn’t cover in CSS yet so lets go into those…
  • 26. NewPostButton { border: 1px #cccccc cn1-pill-border; background-color: white; padding: 1mm 2mm 1mm 2mm; margin: 1mm; color: black; font-family: "native:MainLight"; font-size: 2.6mm; } GalleryButton { color: #4B4F56; font-family: "native:MainLight"; font-size: 1.5mm; text-align: center; padding: 0px 2mm 0px 2mm; margin: 0px; } HalfPaddedContainer { padding: 1.5mm; margin: 0mm; background: transparent; } theme.css The button looks like a text field, when we press it we go directly to the post editing form
  • 27. NewPostButton { border: 1px #cccccc cn1-pill-border; background-color: white; padding: 1mm 2mm 1mm 2mm; margin: 1mm; color: black; font-family: "native:MainLight"; font-size: 2.6mm; } GalleryButton { color: #4B4F56; font-family: "native:MainLight"; font-size: 1.5mm; text-align: center; padding: 0px 2mm 0px 2mm; margin: 0px; } HalfPaddedContainer { padding: 1.5mm; margin: 0mm; background: transparent; } theme.css The gallery button has small font and padding so it can fit in the same row as the other elements
  • 28. color: #4B4F56; font-family: "native:MainLight"; font-size: 1.5mm; text-align: center; padding: 0px 2mm 0px 2mm; margin: 0px; } HalfPaddedContainer { padding: 1.5mm; margin: 0mm; background: transparent; } CleanButton { cn1-derive: BaseButton; font-family: "native:MainLight"; font-size: 2.5mm; margin: 0px; color: #4B4F56; } PostTitle { font-family: "native:MainRegular"; font-size: 3.5mm; } PostSubTitle { font-family: "native:MainRegular"; theme.css The PaddedContainer UIID is often too heavily padded for some UI elements, this uses 1.5mm instead of 3mm in the standard padding
  • 29. color: #4B4F56; font-family: "native:MainLight"; font-size: 1.5mm; text-align: center; padding: 0px 2mm 0px 2mm; margin: 0px; } HalfPaddedContainer { padding: 1.5mm; margin: 0mm; background: transparent; } CleanButton { cn1-derive: BaseButton; font-family: "native:MainLight"; font-size: 2.5mm; margin: 0px; color: #4B4F56; } PostTitle { font-family: "native:MainRegular"; font-size: 3.5mm; } PostSubTitle { font-family: "native:MainRegular"; theme.css We'll use this in several places as we go on, here it doesn't matter as much as it mostly shows an icon
  • 30. cn1-derive: BaseButton; font-family: "native:MainLight"; font-size: 2.5mm; margin: 0px; color: #4B4F56; } PostTitle { font-family: "native:MainRegular"; font-size: 3.5mm; } PostSubTitle { font-family: "native:MainRegular"; font-size: 2mm; color: #aaaaaa; } SmallBlueCircle { border: cn1-round-border; background-color: blue; padding: 0.5mm; margin: 0px 2mm 0px 1mm; color: white; font-family: "native:MainLight"; font-size: 2mm; } SmallLabel { theme.css The title and subtitle use regular font which makes them standout next to the light font used in the rest of the app
  • 31. color: #4B4F56; } PostTitle { font-family: "native:MainRegular"; font-size: 3.5mm; } PostSubTitle { font-family: "native:MainRegular"; font-size: 2mm; color: #aaaaaa; } SmallBlueCircle { border: cn1-round-border; background-color: blue; padding: 0.5mm; margin: 0px 2mm 0px 1mm; color: white; font-family: "native:MainLight"; font-size: 2mm; } SmallLabel { font-family: "native:MainLight"; font-size: 2mm; } theme.css This is literally a round blue circle with enough padding to show the thumb within the circle
  • 32. color: #4B4F56; } PostTitle { font-family: "native:MainRegular"; font-size: 3.5mm; } PostSubTitle { font-family: "native:MainRegular"; font-size: 2mm; color: #aaaaaa; } SmallBlueCircle { border: cn1-round-border; background-color: blue; padding: 0.5mm; margin: 0px 2mm 0px 1mm; color: white; font-family: "native:MainLight"; font-size: 2mm; } SmallLabel { font-family: "native:MainLight"; font-size: 2mm; } theme.css I just need a label with a font small enough to fit. That's all the CSS entries we need so we can lean back, press play in the IDE and watch the first form element in the application come to life!