A very common UI pattern is the 5 star ranking system. Up until recently we always had the same answer
when developers asked us how to implement it: “Use toggle buttons
(CheckBox)”.
This is still not a bad answer but we think there is a “better” simpler way to do this thru the
Slider which was
effectively designed with this usage in mind.
The best way to do that is to just create two images with all 5 stars full and with all 5 stars empty and assign
this to the Slider
/SliderFull
UIID’s. Keep in mind that you need to apply both to the selected and unselected
states of the UIID’s.
You can change the UIID of slider itself e.g. to something like “Stars” at which point the UIID’s will beStars & StarsFull .
|
This will allow your users to click/drag to select the number of stars. The code below uses the star material icon
to generate something like this on the fly without any resources.
We enclose the slider in a FlowLayout to prevent it from growing. This is because I chose the stars to be tiledinstead of aligned (like we could if we used an image). So if the component will grow it won’t have the right feel. Enclosing the component in a FlowLayout is an old trick to prevent components from growing beyond their preferredsize. |
private void initStarRankStyle(Style s, Image star) {
s.setBackgroundType(Style.BACKGROUND_IMAGE_TILE_BOTH);
s.setBorder(Border.createEmpty());
s.setBgImage(star);
s.setBgTransparency(0);
}
private Slider createStarRankSlider() {
Slider starRank = new Slider();
starRank.setEditable(true);
starRank.setMinValue(0);
starRank.setMaxValue(10);
Font fnt = Font.createTrueTypeFont("native:MainLight", "native:MainLight").
derive(Display.getInstance().convertToPixels(5, true), Font.STYLE_PLAIN);
Style s = new Style(0xffff33, 0, fnt, (byte)0);
Image fullStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage();
s.setOpacity(100);
s.setFgColor(0);
Image emptyStar = FontImage.createMaterial(FontImage.MATERIAL_STAR, s).toImage();
initStarRankStyle(starRank.getSliderEmptySelectedStyle(), emptyStar);
initStarRankStyle(starRank.getSliderEmptyUnselectedStyle(), emptyStar);
initStarRankStyle(starRank.getSliderFullSelectedStyle(), fullStar);
initStarRankStyle(starRank.getSliderFullUnselectedStyle(), fullStar);
starRank.setPreferredSize(new Dimension(fullStar.getWidth() * 5, fullStar.getHeight()));
return starRank;
}
private void showStarPickingForm() {
Form hi = new Form("Star Slider", new BoxLayout(BoxLayout.Y_AXIS));
hi.add(FlowLayout.encloseCenter(createStarRankSlider()));
hi.show();
}
In this code you will notice we allow selecting a value between 0 & 10 where 10 is really 5 stars. This allows us
to pick values like 4.5 stars and just divide the actual value. However, most ranking systems don’t allow a value
below 1 star. To solve this you can just use a Label
to represent the first star and use a the Slider
for the remaining
4 stars. In which case the values would be between 0 – 8.
Terse commands
I love lambdas. I wasn’t a fan before they were introduced but they grew on me and made me a convert.
One of the annoyances I had with Codename One was with using Command
syntax which forced me to fallback
to pre-lambda code for practically everything as Command
is a class and not a single method interface. This
bothered me enough to do something about it so now we have ActionListener
versions of many Command
API’s.
These all redirect to the
Command.create(String,Image,ActionListener)
method which effectively creates a Command
with the given details for the given action listener. So instead
of writing code like this:
form.getToolbar().addToSideMenu(new Command("My Command") {
public void actionPerformed(ActionEvent ev) {
myCodeHere();
}
});
I can write this:
form.getToolbar().addToSideMenu(Command.create("My Command", null, (ev) -> {
myCodeHere();
}));
And to make things even simpler I created helper methods that do that implicitly in Toolbar
and Form
:
form.getToolbar().addToSideMenu("My Command", null, (ev) -> {
myCodeHere();
});
Notice that the version of this method that accepts a the action listener also returns the created Command
instance
which might be useful if you want to do something with the command later on (e.g. remove it). So this should work:
Command cmd = form.getToolbar().addToSideMenu("My Command", null, (ev) -> {
myCodeHere();
});
9 Comments
Hi Shai,
Please check this page, it’s not aligned.
Hi Diamond,
I just played a bit with the UI for asciidoc conversion of blog posts. What browser/OS combination are you using? Can you provide a screenshot so I can see if we are seeing the same thing?
Chrome on Windows 10 machine.
Here is the screenshot.
Gotcha, that’s a really wide screen. Looking into it.
Just did an update, is it better?
Yes, it’s fixed now.
Thanks!
Just use getProgress() on the Slider component: [http://codenameone.com/java…](http://codenameone.com/javadoc/com/codename1/ui/Slider.html)
Not the most intuitive method name for this case but we initially designed it as a progress indicator…
hello , how can i get and set the values of rating stars?
The guy who asked this 4 months ago deleted his question but my answer is the same:
Just use getProgress() on the Slider component: [http://codenameone.com/java](http://codenameone.com/java)…
Not the most intuitive method name for this case but we initially designed it as a progress indicator…