Forget Pager Indicator Libraries, Use ItemDecoration Instead

Muhammad Wyndham Haryata Permana
Gravel Product & Tech
4 min readMar 19, 2021

--

Imagine you’re an aspiring Android Developer, and you’re tasked to make something like this :

A Pager with indicator on the bottom of the screen (Image courtesy of Tutorial Bird)

And then you’re wondering : “How do I make indicator like that?”

You only want a pager indicator that works, and need nothing too fancy like something with custom animations (ones that can jump, fluid or bouncy) but something that do what it meant to do: Indicate which page you’re in.

But once you starts searching online, the first few answer you got are links to libraries instead. So you’re thinking maybe the correct answer is to use library after all?

Wrong! or at least, you can do something else that doesn’t requires any addition to your already ever expanding list of dependencies. (I know because I am also a dependency hoarder in the past).

And our hero exist in the vanilla Android Libraries itself : ItemDecoration, this post will explains how it works and how to make one.

But first, some caveats :

  • This post assume you’re using RecyclerView or ViewPager2 as the basis of your Pager. Original ViewPager won’t work!
  • You need a little bit of math and some imagination (or pen and paper, whichever suits your taste)

Now, we need to knows first how ItemDecoration works.

ItemDecoration works by attaching itself to a RecyclerView / ViewPager2 and listening to the onDraw() method its parent. By intercepting onDraw() method, ItemDecoration can add additional static view element inside the RecyclerView’s item using Canvas as the “canvas” where it draws the view.

ItemDecoration can be used for many many things, one of which by adding divider that stays static in-between two items, adding static flairs inside the RecyclerView, and so on.

On this occasion, we’re using it to emulates the behavior of a Pager Indicator.

First you need to make a Class that extends RecyclerView.ItemDecoration() :

After that you need to know what you want to make, for this example, I want to make similarly sized dots, with black for selected, and grey for the rest.

This Tutorial’s End result.

For this, we need to create Paint variable that contains information for how we draw the dots. because we have 2 state, selected and deselected, we need 2 Paint variable :

2 Paint variables represent 2 state : Selected and Deselected

After that, we need to figure out where and how we put the dots. This will requires a little bit of math.

First, lets imagine that a single dot can be represented with a single circle with radius of R.

A single circle has a width of exactly 2R.

To put two dots apart, we need distance between the two. Let’s assume the shortest distance between the outermost of two circles as P (stands for Padding).

The width of a single circle until right before the next circle would be exactly:

2R+P

Then the total width for a row of circles with n amount of elements would be

((2R+P)*n)-2P

(The extra -2P meant for removing the last item’s P (Padding) because there is no more circle right next to it).

Then let’s assume we have screen with the width of W. To draw an object to the screen, we need x and y coordinate that indicates the origin of the object.

If the object is row of circles, and we need to draw this row right in the middle of the screen horizontally and slightly above baseline vertically, we need to calculate the x and y for the origin of the object. (bottom left of the object).

To do so, we can calculate x as W minus the width of the object divided by 2 or to put it in equation :

(W-(((2R+P)*n)-2P))/2

and y as any number of pixel you want it above the baseline.

With all of this information in mind, we can get this logic to our code :

This is the implementation of the metaphorical math above

Great! now that we know where to draw the circles, what’s left is to draw the circle onto the screen.

ItemDecoration comes with override method onDraw() which would trigger every time the parent RecyclerView where the ItemDecoration attached to changes in view and redraw the list.

What we need to do in this method is to draw all of the circles, but with the appropriate paint.

If the circle is in the same position as the visible item/page, then it should shows black circle/dot. Otherwise it should shows grey circle/dot.

This can be achieved by looking at the first visible item in the RecyclerView. Luckily, onDraw() method provides us with the parent RecyclerView, so we simply need to access .findFirstVisibleItemPosition() to knows the currently visible item/page.

All of that would results with code like this:

Where the magic happened!

And… you’re (almost) done! All you need is tied it together, and you’re ready to attach this ItemDecoration to almost any RecyclerView (including ViewPager2). you can simply do this:

val itemDecoration = PageDotDecorator(itemList.size, context)val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)recyclerView.addItemDecoration(itemDecoration)

And your new Pager Indicator would be suddenly shows in your Pager!

To finalize, this is the complete code for all of that above if you’re more into the implementation of it :

To Summarize, ItemDecoration is a magic do it all tools to customize your RecyclerView and ViewPager2. You can draw almost anything as long as your imagination is up to task.

This post is one example of ItemDecoration implementation used to add indicator to a Pager system. And you can even animate the selected dots by using translation in accordance to the parent RecyclerView. Though it is outside of this article scope (and admittedly also outside of my ability).

Thank you for reading, and see you in my other posts!

--

--