Creating a Facebook Clone - Part XIII - Transcript.pdf
1. Creating a Facebook Clone - Part XIII
The hardest part in the mockup is behind us. The next stage is the FriendsContainer which lists friend requests and potential friend suggestions.
3. public class FriendsContainer extends Container {
public FriendsContainer() {
super(BoxLayout.y());
setScrollableY(true);
init();
addPullToRefresh(() -> {
removeAll();
init();
revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
FriendsContainer
Lets look at the code…
We extend Container instead of InfiniteContainer as we don’t need it in this case. Technically I could have gone with an infinite approach but I wanted to keep things
simple
4. public class FriendsContainer extends Container {
public FriendsContainer() {
super(BoxLayout.y());
setScrollableY(true);
init();
addPullToRefresh(() -> {
removeAll();
init();
revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
FriendsContainer
The Container arranges elements vertically using box layout, since this isn’t a form it's not scrollable by default so we need to enable scrollability because the parent form
isn't scrollable.
5. public class FriendsContainer extends Container {
public FriendsContainer() {
super(BoxLayout.y());
setScrollableY(true);
init();
addPullToRefresh(() -> {
removeAll();
init();
revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
FriendsContainer
addPullToRefresh removes the elements and re-creates the UI
6. revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
"PaddedContainer");
padded.add(CENTER,
new Label("No new Friend Requests", "CenterLabel"));
} else {
for(User u : ServerAPI.me().friendRequests) {
Image i = URLImage.createCachedImage(u.id.get() +
"-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, true));
add(UIUtils.createHalfSpace());
FriendsContainer
init() fills up the Container. It's invoked when the class is created or refreshed
7. revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
"PaddedContainer");
padded.add(CENTER,
new Label("No new Friend Requests", "CenterLabel"));
} else {
for(User u : ServerAPI.me().friendRequests) {
Image i = URLImage.createCachedImage(u.id.get() +
"-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, true));
add(UIUtils.createHalfSpace());
FriendsContainer
The avatar in the side of the friend appears as an 18 millimeter image
8. revalidate();
});
}
private void init() {
int friendCount = ServerAPI.me().friendRequests.size();
int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
"PaddedContainer");
padded.add(CENTER,
new Label("No new Friend Requests", "CenterLabel"));
} else {
for(User u : ServerAPI.me().friendRequests) {
Image i = URLImage.createCachedImage(u.id.get() +
"-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, true));
add(UIUtils.createHalfSpace());
FriendsContainer
We use a blank image as the placeholder for the URLImage so the avatar will download dynamically to the locale cache
9. int imageSize = convertToPixels(18);
EncodedImage placeholder = EncodedImage.createFromImage(
Image.createImage(imageSize, imageSize), false);
add(createTitle("FRIEND REQUESTS", friendCount));
if(friendCount == 0) {
Container padded = new Container(new BorderLayout(),
"PaddedContainer");
padded.add(CENTER,
new Label("No new Friend Requests", "CenterLabel"));
} else {
for(User u : ServerAPI.me().friendRequests) {
Image i = URLImage.createCachedImage(u.id.get() +
"-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, true));
add(UIUtils.createHalfSpace());
}
}
add(UIUtils.createHalfSpace());
add(createTitle("PEOPLE YOU MAY KNOW", 0));
for(User u : ServerAPI.me().peopleYouMayKnow) {
FriendsContainer
If we have friend suggestions we loop over friend requests and add them with the avatar for each one. Notice we used the URLImage.createCachedImage() API to fetch
the avatar URL instead of getAvatar(). Facebook used square images here so it made sense to use something else for this functionality.
10. padded.add(CENTER,
new Label("No new Friend Requests", "CenterLabel"));
} else {
for(User u : ServerAPI.me().friendRequests) {
Image i = URLImage.createCachedImage(u.id.get() +
"-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, true));
add(UIUtils.createHalfSpace());
}
}
add(UIUtils.createHalfSpace());
add(createTitle("PEOPLE YOU MAY KNOW", 0));
for(User u : ServerAPI.me().peopleYouMayKnow) {
Image i = URLImage.createCachedImage(u.id.get() + "-avatar.jpg",
u.avatar.get(), placeholder,
URLImage.FLAG_RESIZE_SCALE_TO_FILL);
add(friendRequestEntry(u, i, false));
add(UIUtils.createHalfSpace());
}
}
private Container friendRequestEntry(User u, Image avatar,
FriendsContainer
We do the same for the friend suggestion list under a different title
11. add(friendRequestEntry(u, i, false));
add(UIUtils.createHalfSpace());
}
}
private Container friendRequestEntry(User u, Image avatar,
boolean request) {
Label name = new Label(u.fullName(), "FriendName");
Button confirm;
Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
FriendsContainer
Next we have the friendRequestEntry method…
12. add(friendRequestEntry(u, i, false));
add(UIUtils.createHalfSpace());
}
}
private Container friendRequestEntry(User u, Image avatar,
boolean request) {
Label name = new Label(u.fullName(), "FriendName");
Button confirm;
Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
FriendsContainer
We create the button labels based on the type of request
13. add(friendRequestEntry(u, i, false));
add(UIUtils.createHalfSpace());
}
}
private Container friendRequestEntry(User u, Image avatar,
boolean request) {
Label name = new Label(u.fullName(), "FriendName");
Button confirm;
Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
FriendsContainer
We place the name above the buttons using a box layout on the Y axis then place the two buttons within a grid layout giving them the same size
14. add(friendRequestEntry(u, i, false));
add(UIUtils.createHalfSpace());
}
}
private Container friendRequestEntry(User u, Image avatar,
boolean request) {
Label name = new Label(u.fullName(), "FriendName");
Button confirm;
Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
FriendsContainer
The avatar is placed in the west since its size is constant, we place the content in the center which gives it space to grow
15. Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
private Component createTitle(String title, int count) {
Label titleLabel = new Label(title, "FriendSubtitle");
if(count > 0) {
Label countLabel = new Label("" + count, "SmallRedCircle");
return FlowLayout.encloseMiddle(titleLabel, countLabel);
}
return titleLabel;
}
}
FriendsContainer
The titles above the listings are created in the init() method with this code block
16. Button delete;
if(request) {
confirm = new Button("Confirm", "FriendConfirm");
delete = new Button("Delete", "FriendDelete");
} else {
confirm = new Button("Add Friend", "FriendConfirm");
delete = new Button("Remove", "FriendDelete");
}
Container cnt =
BoxLayout.encloseY(name,
GridLayout.encloseIn(2, confirm, delete));
cnt.setUIID("PaddedContainer");
return BorderLayout.centerEastWest(cnt, null,
new Label(avatar, "Container"));
}
private Component createTitle(String title, int count) {
Label titleLabel = new Label(title, "FriendSubtitle");
if(count > 0) {
Label countLabel = new Label("" + count, "SmallRedCircle");
return FlowLayout.encloseMiddle(titleLabel, countLabel);
}
return titleLabel;
}
}
FriendsContainer
This handles the red circle with the number next to the title
18. FriendSubtitle {
color: #999999;
font-size: 3mm;
font-family: "native:MainRegular";
padding: 1.5mm;
}
SmallRedCircle {
border: cn1-round-border;
background-color: red;
padding: 1mm;
margin: 1mm;
color: white;
font-family: "native:MainRegular";
font-size: 2.2mm;
}
FriendName {
color: black;
font-size: 3mm;
font-family: "native:MainLight";
}
FriendConfirm {
padding: 2mm;
theme.css
In order to complete this we need a few CSS changes.
The subtitle is just a gray slightly larger font
19. FriendSubtitle {
color: #999999;
font-size: 3mm;
font-family: "native:MainRegular";
padding: 1.5mm;
}
SmallRedCircle {
border: cn1-round-border;
background-color: red;
padding: 1mm;
margin: 1mm;
color: white;
font-family: "native:MainRegular";
font-size: 2.2mm;
}
FriendName {
color: black;
font-size: 3mm;
font-family: "native:MainLight";
}
FriendConfirm {
padding: 2mm;
theme.css
The red circle has padding that is large enough to make it visible. The size is close enough so it will align reasonably with the FriendSubtitle