A lightweight tooltip popup for Android.

Jaewoong Eum
The Startup
Published in
4 min readDec 27, 2019

This posting is about how to implement a lightweight popup like tooltips, fully customizable with arrow and animations using Balloon library. The library provides kotlin dsl, many kinds of showing functions based on aligning, showing sequentially functions, arrow, text compositions, lazy initialization, preferences, and some functions for avoiding memory leak.

The balloon is one of the most popular libraries I have ever published.

The balloon is downloaded from more than 27,000 projects every month over the world. Thank you for your guy's interests :)

  • 03.26 2022–200,000 downloads by month

And let’s start!

Including in your project

Firstly you should add a dependency code to your module’s build.gradle file.

dependencies {
implementation "com.github.skydoves:balloon:1.4.7"
}

The version could be different from the latest version.
If you want to use the latest version, you can check it here.

How to use

Here is a basic example of implementing balloon popup with icon and text using Balloon.Builder class based on Java.

This is how to create aBalloon instance using kotlin dsl.

There are more functions related to width, height, padding, icon, an arrow (position, size, drawable, orientation), background, radius, text color, icon, listeners, etc.

Show and dismiss

The balloon provides many kinds of the show and the dismiss functions.

Or we can show balloon popup using kotlin extension like below.

myButtom.showAlignTop(balloon)

We can dismiss simply usingdismissmethod.

balloon.dismiss()
balloon.dismissWithDelay(1000L) // dismisses 1000 milliseconds later when the popup is shown

We can dismiss automatically some milliseconds later when the popup is shown usingsetAutoDismissDuration method on Balloon.Builder.

Balloon.Builder(baseContext)
// dismisses automatically 1000 milliseconds later when the popup is shown.
.setAutoDismissDuration(1000L)
...

Show sequentially

Sometimes we want to show up the balloon sequentially for providing tooltips to users. We can suppose the below circumstances.

Balloon A Show -> (Balloon A onDismiss) -> Balloon B Show ->
(Balloon B Dismiss) -> Balloon C Show -> (Balloon C Dismiss) -> next step.

We can show balloon popup sequentially using relayShow method.
The relayShow method makes that setOnDismissListener of the first balloon is reset to show the next balloon and returns an instance of the next balloon.

Arrow Composition

We can customize the arrow on the balloon popup. We can customize the visibility, size, position based on ratio, orientation and drawable.

.setArrowVisible(true) // sets the visibility of the arrow.
.setArrowSize(10) // sets the arrow size.
.setArrowPosition(0.8f) // sets the arrow position using the popup size's ratio (0 ~ 1.0)
.setArrowOrientation(ArrowOrientation.TOP) // sets the arrow orientation. top, bottom, left, right
.setArrowDrawable(ContextCompat.getDrawable(baseContext, R.drawable.arrow)) // sets the arrow drawable.

Below previews are implemented using setArrowOrientation and setArrowPosition methods.setArrowPosition measures the balloon popup size and sets the arrow's position using the ratio value.

Text Composition

We can customize the text on the balloon popup. We can set the text string message, size, typeface, color.

.setText("You can edit your profile now!")
.setTextSize(15f)
.setTextTypeface(Typeface.BOLD)
.setTextColor(ContextCompat.getColor(baseContext, R.color.white_87))

Listeners

We can listen to the balloon popup is clicked, dismissed or touched outside using listeners.

We can simplify it using kotlin lambda.

.setOnBalloonClickListener { Toast.makeText(baseContext, "clicked", Toast.LENGTH_SHORT).show() }
.setOnBalloonDismissListener { Toast.makeText(baseContext, "dismissed", Toast.LENGTH_SHORT).show() }
.setOnBalloonOutsideTouchListener { Toast.makeText(baseContext, "touched outside", Toast.LENGTH_SHORT).show() }

Customized layout

We can fully customize the balloon layout using the below method.

.setLayout(R.layout.my_ballon_layout)

Firstly create an xml layout file like layout_custom_profile on your taste.

And next, we can get the inflated custom layout using getContentView method.

val button: Button = 
balloon.getContentView().findViewById(R.id.button_edit)
button.setOnClickListener {
Toast.makeText(baseContext, "Edit", Toast.LENGTH_SHORT).show()
balloon.dismiss()
}

Avoid Memory leak

Dialog, PopupWindow and etc.. have memory leak issue if not dismissed before activity or fragment are destroyed. But Lifecycles are now integrated with the Support Library since Architecture Components 1.0 Stable released.
So we can solve the memory leak issue so easily.

Just use setLifecycleOwner method. Then dismiss method will be called automatically before activity or fragment would be destroyed.

.setLifecycleOwner(lifecycleOwner)

Lazy initialization

We can initialize the balloon property lazily using balloon keyword and Balloon.Factory abstract class. The balloon extension keyword can be used on Activity and Fragment.

Before
CustomActivity.kt

class CustomActivity : AppCompatActivity() {
private val profileBalloon by lazy { BalloonUtils.getProfileBalloon(context = this, lifecycleOwner = this) }
// ...
}

After
CustomActivity.kt

class CustomActivity : AppCompatActivity() {
private val profileBalloon by balloon(ProfileBalloonFactory::class)
// ...
}

We should create a class which extends Balloon.Factory.
An implementation class of the factory must have a default(non-argument) constructor.

That’s all!
If you want to get more information about the balloon library, you can check below the GitHub link. Thank you so much :)

--

--

Jaewoong Eum
The Startup

Senior Android Developer Advocate @ Stream 🥑 • GDE for Android • OSS engineer. Love psychology, magic tricks, and writing poems. https://github.com/skydoves