Skip to content

Repository files navigation

✨ VSpot

Professional Android Spotlight, Feature Discovery & Product Tour Library

Build beautiful onboarding flows, feature highlights, guided tours, coach marks, and contextual user education screens with a clean Java API.


Maven Central

Version 4.0.0Android View SystemLicenseSponsor



VSpot v4.0.0 is a modern, production-ready Android spotlight overlay library designed for onboarding screens, feature discovery, app walkthroughs, and interactive product tours.

It is lightweight, Java-friendly, customizable, and built for real Android apps that need a polished onboarding experience without heavy dependencies.


📌 Table of Contents


🚀 Overview

VSpot helps Android developers highlight important UI elements with a beautiful dimmed overlay, spotlight cutout, message card, step indicator, connector line, and optional action controls.

Use it for:

  • App onboarding
  • Product walkthroughs
  • Feature discovery
  • First-time user education
  • Coach marks
  • Highlighting buttons, cards, forms, icons, images, and custom views
  • Explaining new updates after an app release
  • Guiding users through complex screens

VSpot is built for the classic Android View system and works smoothly with Java-based Android projects.


✨ What is VSpot?

VSpot creates an overlay above your Activity and highlights a target view using a transparent spotlight area.

It can show a message card near the target view with:

  • Title
  • Description
  • Step number
  • Back button
  • Next button
  • Done button
  • Skip button
  • Connector line
  • Target stroke
  • Pulse effect

You can show one spotlight or create a complete multi-step guided tour.


🆕 What is New in v4.0.0

Version 4.0.0 is a major professional upgrade focused on real-world production usage, cleaner APIs, safer lifecycle handling, and better UI customization.

Major v4 Features

FeatureDescription
Multi-step toursCreate full onboarding flows using addStep(...).
Modern message cardRounded, elevated, Material-style message card.
Step indicatorShows progress like 1 / 5.
Action buttonsBuilt-in Back, Next, Done, and Skip controls.
Circle spotlightPerfect for icons, avatars, profile images, and FABs.
Rounded rectangle spotlightGreat for buttons, cards, inputs, and layouts.
Connector lineDraws a clean line between target and message card.
Target strokeAdds a professional border around the highlighted target.
Pulse effectAdds subtle focus around the target area.
Per-step settingsEach step can have its own shape, padding, gravity, and dismiss mode.
Theme APICustomize colors, radius, text sizes, card width, animation, and controls.
Accessibility supportAnnounces step content for accessibility services.
Safe layout listener cleanupPrevents unnecessary listener leaks.
Better drawing performanceNo bitmap allocation inside onDraw().
Safer touch handlingUses overlay-local target/message coordinates.
Backward compatible APIExisting setTargetView, setTitle, setContentText, and old enum names still work.

💎 Why Use VSpot?

Many onboarding libraries are either too heavy, outdated, hard to style, or focused only on simple one-time hints. VSpot v4.0.0 is designed to be small, flexible, and production-friendly.

Developer Benefits

  • Simple Java API
  • No complex setup
  • No XML dependency required for the overlay
  • Works with any existing View
  • Supports single and multi-step flows
  • Clean callbacks for analytics and app logic
  • Customizable without subclassing
  • Easy to publish as a reusable Android library

UI/UX Benefits

  • Professional spotlight overlay
  • Attractive message cards
  • Smooth animations
  • Clear visual connection between message and target
  • User-friendly Back, Next, Skip, and Done flow
  • Better onboarding completion experience

📸 Screenshots

VSpot Smart Text Highlight
VSpot Smart Text Highlight
Highlight any TextView, Button, ImageView, or custom view with a polished overlay.
VSpot Image & Icon Support
Image & Icon Support
VSpot v4.0.0 works with images and dynamic layouts without manual x/y calculations.
VSpot Action Area
Action Area
Use this for CTA buttons, filters, checkout buttons, or important actions..
VSpot Form Fields
Form Fields
Great for onboarding users through profile forms, search boxes, and input fields..
VSpot Grouped Views
Grouped Views
You can target an entire layout group, not only a single small child view..

📦 Installation

Maven Central

Add Maven Central in your project-level settings.gradle or settings.gradle.kts.

Gradle Groovy DSL

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name ="YourProjectName"
include ':app'

Add VSpot in your app module build.gradle:

dependencies {
implementation "io.github.tutorialsandroid:vspot:4.0.0"
}

Gradle Kotlin DSL

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name ="YourProjectName"
include(":app")

Add VSpot in your app module build.gradle.kts:

dependencies {
implementation("io.github.tutorialsandroid:vspot:4.0.0")
}

🔗 JitPack Installation

Add JitPack in your project-level settings.gradle:

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}

Add the dependency:

dependencies {
implementation "com.github.TutorialsAndroid:VSpot:4.0.0"
}

For Kotlin DSL:

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
dependencies {
implementation("com.github.TutorialsAndroid:VSpot:4.0.0")
}

⚙️ Requirements

RequirementValue
PlatformAndroid
LanguageJava
UI systemAndroid View system
Dependency styleMaven Central / JitPack / local module
AndroidX required by libraryNo
Activity context requiredYes

VSpot must be created using an Activity context because it attaches the overlay to the Activity decor view.


⚡ Quick Start

Import VSpot:

importcom.developer.spoti.vspoti.VSpotView;

Show a spotlight:

ViewtargetButton = findViewById(R.id.btnContinue);
newVSpotView.Builder(this)
.setTargetView(targetButton)
.setTitle("Continue Button")
.setContentText("Tap here to continue to the next step.")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.targetView)
.show();

That is it. VSpot will automatically attach itself above your Activity content and highlight the selected view.


🎯 Single Spotlight Example

Use this when you want to explain one specific UI element.

finalViewprofileButton = findViewById(R.id.profileButton);
VSpotViewspot = newVSpotView.Builder(this)
.setTargetView(profileButton)
.setTitle("Your Profile")
.setContentText("Manage your account, preferences, and app settings from here.")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.outside)
.setAccentColor(0xFF6750A4)
.build();
spot.show();

🧭 Multi-Step Product Tour

Use addStep(...) to build a complete onboarding tour.

ViewsearchView = findViewById(R.id.searchView);
ViewfilterButton = findViewById(R.id.filterButton);
ViewprofileButton = findViewById(R.id.profileButton);
ViewcreateButton = findViewById(R.id.createButton);
VSpotViewtour = newVSpotView.Builder(this)
.addStep(searchView,
"Smart Search",
"Quickly find content, users, or features using the search bar.")
.addStep(filterButton,
"Powerful Filters",
"Refine your results with category, date, and status filters.")
.addStep(profileButton,
"Your Profile",
"Manage your personal information and app preferences.")
.addStep(createButton,
"Create New Item",
"Tap here whenever you want to create something new.")
.setDismissType(VSpotView.DismissType.none)
.setGravity(VSpotView.Gravity.center)
.setAccentColor(0xFF6750A4)
.show();

Why use DismissType.none for tours?

For guided onboarding, it is better to keep users inside the tour flow and let them use Next, Back, Done, or Skip. This prevents accidental dismissals.


🎨 Professional Theme Example

You can create a premium-looking branded tour by customizing the visual style.

VSpotView.Stylestyle = newVSpotView.Style();
style.overlayColor = 0xE6000000;
style.cardColor = 0xFFFFFFFF;
style.cardStrokeColor = 0x1F000000;
style.titleColor = 0xFF111827;
style.contentColor = 0xFF4B5563;
style.accentColor = 0xFF6750A4;
style.stepBadgeColor = 0xFF6750A4;
style.stepBadgeTextColor = 0xFFFFFFFF;
style.connectorColor = 0xFFFFFFFF;
style.targetStrokeColor = 0xFFFFFFFF;
style.targetPulseColor = 0x66FFFFFF;
style.cardCornerRadiusDp = 24f;
style.cardElevationDp = 14f;
style.cardShadowDyDp = 5f;
style.targetCornerRadiusDp = 18f;
style.spotlightPaddingDp = 10f;
style.maxMessageWidthDp = 360;
style.titleTextSizeSp = 19;
style.contentTextSizeSp = 14;
style.buttonTextSizeSp = 14;
style.stepTextSizeSp = 12;
style.showControls = true;
style.showConnector = true;
style.showTargetStroke = true;
style.showPulse = true;
style.showStepIndicator = true;
style.showPreviousButton = true;
style.showSkipButton = true;
style.previousButtonText = "Back";
style.nextButtonText = "Next";
style.doneButtonText = "Done";
style.skipButtonText = "Skip";
style.animationDuration = 260L;
VSpotViewtour = newVSpotView.Builder(this)
.setStyle(style)
.addStep(findViewById(R.id.view1), "Welcome", "Let us quickly show you around.")
.addStep(findViewById(R.id.view2), "Explore", "This section helps you discover important actions.")
.addStep(findViewById(R.id.view3), "You are ready", "Start using the app with confidence.")
.setDismissType(VSpotView.DismissType.none)
.show();

🧩 Per-Step Customization

Each step can override global settings such as shape, padding, gravity, and dismiss behavior.

VSpotView.StepsearchStep = newVSpotView.Step.Builder(findViewById(R.id.searchView))
.setTitle("Search Anything")
.setContentText("Use this search field to quickly find what you need.")
.setGravity(VSpotView.Gravity.center)
.setSpotlightShape(VSpotView.SpotlightShape.roundedRectangle)
.setSpotlightPaddingDp(8)
.build();
VSpotView.StepavatarStep = newVSpotView.Step.Builder(findViewById(R.id.avatarView))
.setTitle("Profile")
.setContentText("Tap your avatar to open account settings.")
.setGravity(VSpotView.Gravity.right)
.setSpotlightShape(VSpotView.SpotlightShape.circle)
.setSpotlightPaddingDp(10)
.build();
newVSpotView.Builder(this)
.addStep(searchStep)
.addStep(avatarStep)
.setDismissType(VSpotView.DismissType.none)
.show();

🔔 Callbacks

Use callbacks to track analytics, update UI, or run logic when the tour starts, changes step, clicks the target, or ends.

VSpotViewtour = newVSpotView.Builder(this)
.addStep(findViewById(R.id.view1), "Step One", "This is the first feature.")
.addStep(findViewById(R.id.view2), "Step Two", "This is the second feature.")
.setCallback(newVSpotView.Callback() {
@OverridepublicvoidonShow(VSpotViewspotView) {
// Tour became visible.
}
@OverridepublicvoidonStepChanged(ViewtargetView, intstepIndex, VSpotView.Stepstep) {
// Called whenever the active step changes.// Useful for analytics: onboarding_step_viewed.
}
@OverridepublicvoidonTargetClick(ViewtargetView, intstepIndex, VSpotView.Stepstep) {
// Called when the target view is clicked using DismissType.targetView.
}
@OverridepublicvoidonDismiss(ViewlastTargetView, booleancompleted, intlastStepIndex) {
if (completed) {
// User completed the tour.
} else {
// User skipped or dismissed the tour.
}
}
})
.show();

🎮 Manual Controls

VSpot exposes useful control methods for advanced use cases.

VSpotViewtour = newVSpotView.Builder(this)
.addStep(findViewById(R.id.view1), "One", "First step")
.addStep(findViewById(R.id.view2), "Two", "Second step")
.addStep(findViewById(R.id.view3), "Three", "Third step")
.build();
tour.show();
// Go to next steptour.next();
// Go to previous steptour.previous();
// Jump to a specific steptour.goToStep(2);
// Finish as completedtour.finish();
// Dismiss without completiontour.dismiss();

Useful getters:

booleanshowing = tour.isShowing();
intcurrentIndex = tour.getCurrentIndex();
inttotalSteps = tour.getStepCount();
VSpotView.StepcurrentStep = tour.getCurrentStep();
ViewcurrentTarget = tour.getCurrentTargetView();

📝 Spannable Content

You can pass rich text using Spannable or any CharSequence.

SpannableStringcontent = newSpannableString("Use filters to save time and find better results.");
content.setSpan(
newStyleSpan(Typeface.BOLD),
4,
11,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
newVSpotView.Builder(this)
.setTargetView(findViewById(R.id.filterButton))
.setTitle("Smart Filters")
.setContentSpan(content)
.show();

Required imports:

importandroid.graphics.Typeface;
importandroid.text.SpannableString;
importandroid.text.Spanned;
importandroid.text.style.StyleSpan;

👆 Dismiss Modes

VSpot supports multiple dismiss behaviors.

Dismiss TypeBehavior
DismissType.noneUser cannot dismiss by touching overlay. Best for controlled tours.
DismissType.outsideDismisses when user taps outside the message card.
DismissType.anywhereDismisses when user taps anywhere outside the card.
DismissType.targetViewPerforms target click, then dismisses.

Both lowercase and uppercase enum names are supported:

VSpotView.DismissType.noneVSpotView.DismissType.NONEVSpotView.DismissType.outsideVSpotView.DismissType.OUTSIDEVSpotView.DismissType.anywhereVSpotView.DismissType.ANYWHEREVSpotView.DismissType.targetViewVSpotView.DismissType.TARGET_VIEW

Recommended usage:

// Best for onboarding tours
.setDismissType(VSpotView.DismissType.none)
// Best for simple hints
.setDismissType(VSpotView.DismissType.outside)
// Best when you want target click behavior
.setDismissType(VSpotView.DismissType.targetView)

🧲 Gravity Options

Gravity controls horizontal placement of the message card around the highlighted target.

GravityBehavior
Gravity.autoDefault smart positioning.
Gravity.centerCenters message card to target center.
Gravity.leftAligns message card to target left.
Gravity.rightAligns message card to target right.

Both lowercase and uppercase enum names are supported:

VSpotView.Gravity.autoVSpotView.Gravity.centerVSpotView.Gravity.leftVSpotView.Gravity.rightVSpotView.Gravity.AUTOVSpotView.Gravity.CENTERVSpotView.Gravity.LEFTVSpotView.Gravity.RIGHT

Example:

newVSpotView.Builder(this)
.setTargetView(findViewById(R.id.settingsButton))
.setTitle("Settings")
.setContentText("Manage your preferences here.")
.setGravity(VSpotView.Gravity.right)
.show();

🔦 Spotlight Shapes

VSpot supports two spotlight shapes.

ShapeBest For
roundedRectangleButtons, cards, input fields, layout blocks.
circleIcons, avatars, FABs, small image buttons.

Both lowercase and uppercase enum names are supported:

VSpotView.SpotlightShape.roundedRectangleVSpotView.SpotlightShape.circleVSpotView.SpotlightShape.ROUNDED_RECTANGLEVSpotView.SpotlightShape.CIRCLE

Example:

newVSpotView.Builder(this)
.setTargetView(findViewById(R.id.fabAdd))
.setTitle("Create")
.setContentText("Tap here to create a new item.")
.setSpotlightShape(VSpotView.SpotlightShape.circle)
.show();

🎛️ Style API

VSpotView.Style gives full control over the visual design.

Colors

PropertyDescription
overlayColorDim overlay color.
cardColorMessage card background color.
cardStrokeColorMessage card border color.
titleColorTitle text color.
contentColorContent text color.
accentColorPrimary action/accent color.
connectorColorConnector line color.
targetStrokeColorHighlighted target border color.
targetPulseColorTarget pulse color.
stepBadgeColorStep indicator badge background.
stepBadgeTextColorStep indicator badge text color.

Dimensions

PropertyDescription
cardCornerRadiusDpMessage card corner radius.
cardStrokeWidthDpMessage card border width.
cardElevationDpMessage card shadow/elevation strength.
cardShadowDyDpVertical card shadow offset.
targetCornerRadiusDpTarget cutout corner radius.
targetStrokeWidthDpHighlighted target stroke width.
spotlightPaddingDpSpace around highlighted view.
connectorWidthDpConnector line width.
connectorDotRadiusDpConnector dot radius.
indicatorGapDpGap between target and message card.
screenHorizontalMarginDpHorizontal safe margin.
screenVerticalMarginDpVertical safe margin.
maxMessageWidthDpMaximum message card width.

Text

PropertyDescription
titleTextSizeSpTitle text size.
contentTextSizeSpContent text size.
buttonTextSizeSpButton text size.
stepTextSizeSpStep badge text size.
titleTypefaceTitle typeface.
contentTypefaceContent typeface.

Visibility Flags

PropertyDescription
showControlsShow or hide action buttons.
showPreviousButtonShow or hide Back button.
showSkipButtonShow or hide Skip button.
showStepIndicatorShow or hide step badge.
showConnectorShow or hide connector line.
showTargetStrokeShow or hide target border.
showPulseShow or hide pulse outline.

Button Text

PropertyDefault
previousButtonTextBack
nextButtonTextNext
doneButtonTextDone
skipButtonTextSkip

Animation

PropertyDescription
animationDurationOverlay animation duration in milliseconds.
startScaleOverlay scale at show start.
endScaleOverlay scale at dismiss end.

🏗️ Builder API

Core Methods

MethodDescription
setTargetView(View view)Sets target view for a single spotlight.
setTitle(String title)Sets title for single spotlight mode.
setContentText(String contentText)Sets plain text content.
setContentSpan(Spannable span)Sets spannable content.
setContent(CharSequence content)Sets any CharSequence content.
addStep(View targetView, String title, String contentText)Adds a tour step with plain string content.
addStep(View targetView, String title, CharSequence content)Adds a tour step with rich text content.
addStep(VSpotView.Step step)Adds a fully customized step.
setGravity(Gravity gravity)Sets default message gravity.
setDismissType(DismissType dismissType)Sets default dismiss behavior.
setCallback(VSpotView.Callback callback)Sets lifecycle callbacks.
setStyle(VSpotView.Style style)Applies complete custom style.
build()Builds a VSpotView instance.
show()Builds and immediately shows VSpot.

Fast Styling Methods

MethodDescription
setOverlayColor(int color)Sets overlay color.
setCardColor(int color)Sets card color.
setTitleColor(int color)Sets title color.
setContentColor(int color)Sets content color.
setAccentColor(int color)Sets action/step accent color.
setConnectorColor(int color)Sets connector line color.
setTargetStrokeColor(int color)Sets highlighted target stroke color.
setCardCornerRadiusDp(float radiusDp)Sets card corner radius.
setTargetCornerRadiusDp(float radiusDp)Sets target corner radius.
setSpotlightPaddingDp(float paddingDp)Sets global spotlight padding.
setSpotlightShape(SpotlightShape shape)Sets global spotlight shape.
setMaxMessageWidthDp(int widthDp)Sets max card width.
setShowControls(boolean showControls)Shows/hides action controls.
setShowConnector(boolean showConnector)Shows/hides connector line.
setShowStepIndicator(boolean showStepIndicator)Shows/hides step badge.
setShowSkipButton(boolean showSkipButton)Shows/hides skip button.
setShowPreviousButton(boolean showPreviousButton)Shows/hides previous button.
setButtonTexts(String previous, String next, String done, String skip)Customizes button labels.
setAnimationDuration(long durationMillis)Sets animation duration.

🎨 Advanced Customization Options

VSpot v4.0.0 provides a complete styling API for developers who want full control over the spotlight overlay, message card, typography, indicator, highlight box, and content alignment.

These options are useful when you want VSpot to match your app branding or when you need different visual styles for different onboarding flows.

Typeface Customization

You can customize title and content fonts using Android Typeface.

This supports:

  • Normal
  • Bold
  • Italic
  • Bold Italic
  • Custom typefaces
VSpotView.Stylestyle = newVSpotView.Style();
style.titleTypeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
style.contentTypeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
newVSpotView.Builder(this)
.setStyle(style)
.setTargetView(myView)
.setTitle("Custom Typeface")
.setContentText("Title and content can use different typefaces.")
.show();

Required import:

importandroid.graphics.Typeface;

Title and Content Color Customization

You can set different colors for the title and content text.

VSpotView.Stylestyle = newVSpotView.Style();
style.titleColor = 0xFF111827;
style.contentColor = 0xFF4B5563;
newVSpotView.Builder(this)
.setStyle(style)
.setTargetView(myView)
.setTitle("Custom Text Colors")
.setContentText("VSpot lets you control title and content text colors.")
.show();

You can also use fast builder methods:

newVSpotView.Builder(this)
.setTargetView(myView)
.setTitle("Custom Colors")
.setContentText("Set title and content colors directly from the builder.")
.setTitleColor(0xFF111827)
.setContentColor(0xFF4B5563)
.show();

Overlay Dark Intensity

The overlay darkness can be customized using overlayColor.

Use the alpha value to control intensity.

VSpotView.Stylestyle = newVSpotView.Style();
// Light dark overlaystyle.overlayColor = 0x99000000;
// Medium dark overlaystyle.overlayColor = 0xCC000000;
// Strong dark overlaystyle.overlayColor = 0xE6000000;
newVSpotView.Builder(this)
.setStyle(style)
.setTargetView(myView)
.setTitle("Overlay Intensity")
.setContentText("Control how dark the non-highlighted area should be.")
.show();

Builder shortcut:

newVSpotView.Builder(this)
.setTargetView(myView)
.setTitle("Dark Overlay")
.setContentText("The background intensity is fully customizable.")
.setOverlayColor(0xE6000000)
.show();

Highlight Box Color

You can customize the highlighted target border color using targetStrokeColor.

VSpotView.Stylestyle = newVSpotView.Style();
style.targetStrokeColor = 0xFFFFFFFF;
style.targetPulseColor = 0x66FFFFFF;
style.showTargetStroke = true;
style.showPulse = true;
newVSpotView.Builder(this)
.setStyle(style)
.setTargetView(myView)
.setTitle("Highlight Box")
.setContentText("Customize the border and pulse around the highlighted view.")
.show();

Builder shortcut:

newVSpotView.Builder(this)
.setTargetView(myView)
.setTitle("Custom Highlight")
.setContentText("The target highlight border color can be customized.")
.setTargetStrokeColor(0xFFFFFFFF)
.show();

Indicator Color Customization

The step indicator badge can be styled using stepBadgeColor and stepBadgeTextColor.

VSpotView.Stylestyle = newVSpotView.Style();
style.stepBadgeColor = 0xFF6750A4;
style.stepBadgeTextColor = 0xFFFFFFFF;
style.showStepIndicator = true;
newVSpotView.Builder(this)
.setStyle(style)
.addStep(view1, "Step One", "This is the first step.")
.addStep(view2, "Step Two", "This is the second step.")
.show();

Content Without Title

VSpot v4.0.0 automatically handles content-only messages.

If the title is empty or not provided, the content remains cleanly centered inside the message card.

newVSpotView.Builder(this)
.setTargetView(myView)
.setContentText("This message has content only and no title.")
.setShowControls(false)
.setShowStepIndicator(false)
.show();

You can also create content-only steps:

newVSpotView.Builder(this)
.addStep(newVSpotView.Step.Builder(myView)
.setContentText("This step has no title, only centered content.")
.build())
.show();

Complete Customization Example

VSpotView.Stylestyle = newVSpotView.Style();
style.overlayColor = 0xE6000000;
style.cardColor = 0xFFFFFFFF;
style.cardStrokeColor = 0x1F000000;
style.cardCornerRadiusDp = 24f;
style.titleColor = 0xFF111827;
style.contentColor = 0xFF4B5563;
style.titleTypeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
style.contentTypeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
style.targetStrokeColor = 0xFFFFFFFF;
style.targetPulseColor = 0x66FFFFFF;
style.stepBadgeColor = 0xFF6750A4;
style.stepBadgeTextColor = 0xFFFFFFFF;
style.connectorColor = 0xFFFFFFFF;
style.accentColor = 0xFF6750A4;
style.showTargetStroke = true;
style.showPulse = true;
style.showStepIndicator = true;
style.showConnector = true;
newVSpotView.Builder(this)
.setStyle(style)
.setTargetView(myView)
.setTitle("Fully Customizable")
.setContentText("Customize typefaces, text colors, overlay intensity, highlight box, indicator, and more.")
.show();

🔁 Migration Guide

From old single-step API to v4.0.0

Old usage:

VSpotViewvSpot = newVSpotView.Builder(this)
.setTitle("Guide Title")
.setContentText("Guide description")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.outside)
.setTargetView(view1)
.build();
vSpot.show();

New v4 usage still supports the same style:

VSpotViewvSpot = newVSpotView.Builder(this)
.setTargetView(view1)
.setTitle("Guide Title")
.setContentText("Guide description")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.outside)
.build();
vSpot.show();

Recommended v4 tour usage

Instead of manually building the next spotlight inside onDismiss, use addStep(...):

newVSpotView.Builder(this)
.addStep(view1, "Step 1", "This is the first feature.")
.addStep(view2, "Step 2", "This is the second feature.")
.addStep(view3, "Step 3", "This is the third feature.")
.setDismissType(VSpotView.DismissType.none)
.show();

What changed internally in v4

  • Better layout calculation
  • Safer Activity context resolution
  • Cleaner callback system
  • No repeated bitmap creation during drawing
  • Global layout listener cleanup
  • More predictable target and overlay touch handling
  • Professional UI controls built into the message card

✅ Best Practices

1. Show VSpot after layout is ready

If your target view is inside a complex layout, RecyclerView, ViewPager, or dynamic screen, call VSpot after the view has been measured.

findViewById(R.id.myTarget).post(newRunnable() {
@Overridepublicvoidrun() {
newVSpotView.Builder(MainActivity.this)
.setTargetView(findViewById(R.id.myTarget))
.setTitle("Ready")
.setContentText("This view is now ready to highlight.")
.show();
}
});

2. Use DismissType.none for onboarding tours

.setDismissType(VSpotView.DismissType.none)

This avoids accidental dismissal and keeps the flow controlled.

3. Use circle shape only for compact targets

Circle spotlight works best for icons, avatars, FABs, and small image buttons.

.setSpotlightShape(VSpotView.SpotlightShape.circle)

4. Keep content short and clear

Good onboarding content should be easy to read.

Recommended:

Create posts, alerts, and reports from here.

Avoid:

This button is used for the purpose of creating many different types of content and it can be clicked whenever you want to perform the creation process.

5. Track completion

Use callbacks to save whether the user completed or skipped onboarding.

.setCallback(newVSpotView.Callback() {
@OverridepublicvoidonDismiss(ViewlastTargetView, booleancompleted, intlastStepIndex) {
getSharedPreferences("app_prefs", MODE_PRIVATE)
.edit()
.putBoolean("home_tour_completed", completed)
.apply();
}
})

6. Show onboarding only once

SharedPreferencesprefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
booleancompleted = prefs.getBoolean("home_tour_completed", false);
if (!completed) {
newVSpotView.Builder(this)
.addStep(findViewById(R.id.view1), "Welcome", "Let us show you around.")
.addStep(findViewById(R.id.view2), "Explore", "Discover important actions here.")
.setDismissType(VSpotView.DismissType.none)
.setCallback(newVSpotView.Callback() {
@OverridepublicvoidonDismiss(ViewlastTargetView, booleancompleted, intlastStepIndex) {
prefs.edit().putBoolean("home_tour_completed", true).apply();
}
})
.show();
}

🧪 Complete Activity Example

packagecom.example.app;
importandroid.content.SharedPreferences;
importandroid.os.Bundle;
importandroid.view.View;
importandroidx.appcompat.app.AppCompatActivity;
importcom.developer.spoti.vspoti.VSpotView;
publicclassMainActivityextendsAppCompatActivity {
privateVSpotViewonboardingTour;
@OverrideprotectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
finalViewsearchView = findViewById(R.id.searchView);
finalViewfilterButton = findViewById(R.id.filterButton);
finalViewprofileButton = findViewById(R.id.profileButton);
finalViewcreateButton = findViewById(R.id.createButton);
searchView.post(newRunnable() {
@Overridepublicvoidrun() {
showOnboardingIfNeeded(searchView, filterButton, profileButton, createButton);
}
});
}
privatevoidshowOnboardingIfNeeded(ViewsearchView,
ViewfilterButton,
ViewprofileButton,
ViewcreateButton) {
finalSharedPreferencesprefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
booleanalreadyCompleted = prefs.getBoolean("home_tour_completed", false);
if (alreadyCompleted) {
return;
}
VSpotView.Stylestyle = newVSpotView.Style();
style.accentColor = 0xFF6750A4;
style.stepBadgeColor = 0xFF6750A4;
style.cardCornerRadiusDp = 24f;
style.targetCornerRadiusDp = 18f;
style.spotlightPaddingDp = 10f;
style.maxMessageWidthDp = 360;
style.showControls = true;
style.showConnector = true;
style.showTargetStroke = true;
style.showPulse = true;
onboardingTour = newVSpotView.Builder(this)
.setStyle(style)
.addStep(searchView,
"Smart Search",
"Quickly find anything inside the app using search.")
.addStep(filterButton,
"Filters",
"Use filters to narrow results and save time.")
.addStep(newVSpotView.Step.Builder(profileButton)
.setTitle("Profile")
.setContentText("Manage your account and preferences from here.")
.setSpotlightShape(VSpotView.SpotlightShape.circle)
.setSpotlightPaddingDp(10)
.build())
.addStep(createButton,
"Create New",
"Tap here to create your first item.")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.none)
.setCallback(newVSpotView.Callback() {
@OverridepublicvoidonDismiss(ViewlastTargetView, booleancompleted, intlastStepIndex) {
prefs.edit()
.putBoolean("home_tour_completed", true)
.apply();
}
})
.build();
onboardingTour.show();
}
@OverrideprotectedvoidonDestroy() {
if (onboardingTour != null && onboardingTour.isShowing()) {
onboardingTour.dismiss();
}
super.onDestroy();
}
}

🛠️ Troubleshooting

VSpot is not showing

Make sure:

  • You are using an Activity context, not application context.
  • Target view is not null.
  • Target view is already measured.
  • You call VSpot after setContentView(...).

Correct:

newVSpotView.Builder(this)
.setTargetView(findViewById(R.id.myButton))
.setTitle("Button")
.setContentText("This is a highlighted button.")
.show();

Incorrect:

newVSpotView.Builder(getApplicationContext())
.setTargetView(findViewById(R.id.myButton))
.show();

Target view position is wrong

Use post(...) so the target view has completed layout.

Viewtarget = findViewById(R.id.myButton);
target.post(newRunnable() {
@Overridepublicvoidrun() {
newVSpotView.Builder(MainActivity.this)
.setTargetView(target)
.setTitle("Ready")
.setContentText("Now the position is calculated correctly.")
.show();
}
});

Spotlight is too tight

Increase spotlight padding:

.setSpotlightPaddingDp(14f)

Or per step:

newVSpotView.Step.Builder(targetView)
.setTitle("Feature")
.setContentText("Description")
.setSpotlightPaddingDp(14)
.build();

Message card is too wide

Set max message width:

.setMaxMessageWidthDp(300)

User can dismiss the tour accidentally

Use:

.setDismissType(VSpotView.DismissType.none)

I want only a simple highlight without buttons

Use:

newVSpotView.Builder(this)
.setTargetView(findViewById(R.id.myTarget))
.setTitle("Simple Hint")
.setContentText("This is a simple spotlight message.")
.setShowControls(false)
.setShowStepIndicator(false)
.setDismissType(VSpotView.DismissType.outside)
.show();

📚 API Summary

newVSpotView.Builder(activity)
.setTargetView(view)
.setTitle("Title")
.setContentText("Content")
.setGravity(VSpotView.Gravity.center)
.setDismissType(VSpotView.DismissType.outside)
.setSpotlightShape(VSpotView.SpotlightShape.roundedRectangle)
.setSpotlightPaddingDp(8f)
.setAccentColor(0xFF6750A4)
.setCardCornerRadiusDp(22f)
.setMaxMessageWidthDp(340)
.setShowControls(true)
.setShowConnector(true)
.setShowStepIndicator(true)
.setButtonTexts("Back", "Next", "Done", "Skip")
.setAnimationDuration(260L)
.show();

🧱 Local Module Usage

If you are using VSpot as a local Android library module:

settings.gradle

include ':app'
include ':vspoti'

app/build.gradle

dependencies {
implementation project(':vspoti')
}

📣 Versioning

VSpot follows semantic versioning:

MAJOR.MINOR.PATCH

Example:

4.0.0

Meaning:

  • 4 = Major version with important API/features update
  • 0 = Minor feature version
  • 0 = Patch/fix version

🧾 Changelog

v4.0.0

  • Added multi-step guided tours
  • Added Back, Next, Done, and Skip controls
  • Added step indicator badge
  • Added professional card UI
  • Added style customization API
  • Added circle spotlight support
  • Added rounded rectangle spotlight support
  • Added connector line between target and card
  • Added target stroke and pulse effect
  • Added accessibility announcements
  • Added callback system
  • Added manual controls: next(), previous(), goToStep(), finish(), dismiss()
  • Added per-step shape, padding, gravity, and dismiss behavior
  • Improved layout positioning
  • Improved touch handling
  • Improved lifecycle cleanup
  • Removed expensive bitmap allocation from drawing cycle
  • Kept old single-step builder API compatible

🤝 Contributing

Contributions are welcome.

You can help by:

  • Reporting bugs
  • Suggesting new features
  • Improving documentation
  • Adding sample screens
  • Testing on different Android versions and screen sizes
  • Creating pull requests

Development Flow

git clone https://github.com/TutorialsAndroid/VSpot.git
cd VSpot

Open the project in Android Studio, run the sample app, make your changes, and submit a pull request.


⭐ Support the Project

If VSpot helps you build better Android apps, please consider supporting the project.

  • Star the repository
  • Share it with other Android developers
  • Use it in your apps
  • Report issues clearly
  • Contribute improvements
  • Sponsor the project on GitHub
Sponsor VSpot

📄 License

MIT License
Copyright (c) TutorialsAndroid
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Made with ❤️ for Android Developers

VSpot — Beautiful feature discovery and onboarding tours for Android apps.


GitHubIssuesReleasesSponsor

About

Professional Android spotlight overlay library for onboarding flows, feature discovery, guided tours, coach marks, and contextual UI highlights.

Topics

Resources

Stars

29 stars

Watchers

3 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages