The FastScroller
implementation for ListView
varies by Android version. Prior to KitKat (API 19), the thumb is a Drawable
that's drawn directly on the ListView
. Starting with KitKat, the thumb is an ImageView
that's added to the ListView
's ViewGroupOverlay
. In either case, it's easy enough to get what we need through reflection.
Since the ultimate goal is to use this with ShowcaseView
, it makes sense to just concern ourselves with the dimensions and coordinates of the thumb, regardless of its specific type. In this way, we can use ShowcaseView
's PointTarget
, no matter the Android version.
The following reflective method grabs a ListView
's FastScroller
instance, determines the thumb's size and location using the appropriate type, and returns a Point
object with the coordinates of the thumb's center point, if possible.
private Point getFastScrollThumbPoint(final ListView listView) {
try {
final Class<?> fastScrollerClass = Class.forName("android.widget.FastScroller");
final int[] listViewLocation = new int[2];
listView.getLocationInWindow(listViewLocation);
int x = listViewLocation[0];
int y = listViewLocation[1];
final Field fastScrollerField;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
fastScrollerField = AbsListView.class.getDeclaredField("mFastScroll");
}
else {
fastScrollerField = AbsListView.class.getDeclaredField("mFastScroller");
}
fastScrollerField.setAccessible(true);
final Object fastScroller = fastScrollerField.get(listView);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
final Field thumbImageViewField = fastScrollerClass.getDeclaredField("mThumbImage");
thumbImageViewField.setAccessible(true);
final ImageView thumbImageView = (ImageView) thumbImageViewField.get(fastScroller);
final int[] thumbViewLocation = new int[2];
thumbImageView.getLocationInWindow(thumbViewLocation);
x += thumbViewLocation[0] + thumbImageView.getWidth() / 2;
y += thumbViewLocation[1] + thumbImageView.getHeight() / 2;
}
else {
final Field thumbDrawableField = fastScrollerClass.getDeclaredField("mThumbDrawable");
thumbDrawableField.setAccessible(true);
final Drawable thumbDrawable = (Drawable) thumbDrawableField.get(fastScroller);
final Rect bounds = thumbDrawable.getBounds();
final Field thumbYField = fastScrollerClass.getDeclaredField("mThumbY");
thumbYField.setAccessible(true);
final int thumbY = (Integer) thumbYField.get(fastScroller);
x += bounds.left + bounds.width() / 2;
y += thumbY + bounds.height() / 2;
}
return new Point(x, y);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
To use this with ShowcaseView
, we simply check that the returned Point
is not null, and pass the Builder
a PointTarget
created from the return.
Point thumbPoint = getFastScrollThumbPoint(listView);
if (thumbPoint != null) {
new ShowcaseView.Builder(this)
.setTarget(new PointTarget(thumbPoint))
.setContentTitle("ShowcaseView")
.setContentText("This is highlighting the fast scroll thumb")
.hideOnTouchOutside()
.build();
}