SeekBar & RatingBar
SeekBar and RatingBar are both sliders in spirit — the user drags to pick a numeric value — but they look and behave differently enough to deserve separate treatment. Both are genuinely simple widgets, yet it's easy to forget the exact listener method names for each, so this lesson doubles as a quick reference too.
SeekBar: Dragging to Pick a Number
RecipeBox uses a SeekBar on the recipe list screen as a "Prep Time" filter — drag it to only show recipes that can be made in under N minutes:
<SeekBar
android:id="@+id/sbPrepTimeFilter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="120"
android:progress="60" />
<TextView
android:id="@+id/tvPrepTimeLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Under 60 minutes" />
android:max sets the highest value the bar can reach (120 minutes here); android:progress sets the starting position. Reacting to drags in real time requires an OnSeekBarChangeListener:
SeekBar sbPrepTimeFilter = findViewById(R.id.sbPrepTimeFilter);
TextView tvPrepTimeLabel = findViewById(R.id.tvPrepTimeLabel);
sbPrepTimeFilter.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
tvPrepTimeLabel.setText("Under " + progress + " minutes");
if (fromUser) {
filterRecipesByMaxPrepTime(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
});
- onProgressChanged() fires continuously while dragging, and also whenever setProgress() is called from code.
- The fromUser flag distinguishes a real drag from a programmatic update — check it before triggering expensive work like re-filtering a list, so restoring saved state doesn't accidentally re-trigger a filter pass.
- onStartTrackingTouch/onStopTrackingTouch fire once each, at the beginning and end of a drag gesture — useful if you only want to act once the user lifts their finger.
- All three methods must be implemented; there's no single-method shortcut here, so this listener is usually written as an anonymous class rather than a lambda.
RatingBar: Read-Only Display
RecipeBox shows a recipe's average rating from other users as stars. Since the user shouldn't be able to change someone else's average, this RatingBar is set to be an indicator only:
<RatingBar
android:id="@+id/rbAverageRating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="0.5"
android:isIndicator="true"
android:rating="4.5" />
RatingBar rbAverageRating = findViewById(R.id.rbAverageRating);
rbAverageRating.setRating(recipe.averageRating); // e.g. 4.5f, loaded from the database
RatingBar: Interactive Input
RecipeBox also lets the current user submit their own rating for a recipe they've cooked. This RatingBar is interactive, so it omits isIndicator and listens for changes:
<RatingBar
android:id="@+id/rbMyRating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1"
android:rating="0" />
RatingBar rbMyRating = findViewById(R.id.rbMyRating);
rbMyRating.setOnRatingBarChangeListener((ratingBar, rating, fromUser) -> {
if (fromUser) {
submitUserRating(recipe.id, rating);
}
});
Quick Reference
| Widget | Listener interface | Key method(s) | Key XML attrs |
|---|---|---|---|
| SeekBar | OnSeekBarChangeListener | onProgressChanged, onStartTrackingTouch, onStopTrackingTouch | android:max, android:progress |
| RatingBar | OnRatingBarChangeListener | onRatingChanged | android:numStars, android:stepSize, android:isIndicator |