Balloon 🎈 A lightweight popup like tooltips, fully customizable with arrow and animations.

Last update: May 20, 2022

Balloon


🎈 A lightweight popup like tooltips, fully customizable with arrow and animations.


Google
License API Build Status Medium Profile Javadoc


Including in your project

Maven Central Balloon

Gradle

Add below codes to your root build.gradle file (not your module build.gradle file).

allprojects {
    repositories {
        mavenCentral()
    }
}

And add a dependency code to your module's build.gradle file.

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

SNAPSHOT

Balloon
Snapshots of the current development version of Balloon are available, which track the latest versions.

repositories {
   maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

Usage

Basic Example for Java

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

Balloon balloon = new Balloon.Builder(context)
    .setArrowSize(10)
    .setArrowOrientation(ArrowOrientation.TOP)
    .setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR)
    .setArrowPosition(0.5f)
    .setWidth(BalloonSizeSpec.WRAP)
    .setHeight(65)
    .setTextSize(15f)
    .setCornerRadius(4f)
    .setAlpha(0.9f)
    .setText("You can access your profile from now on.")
    .setTextColor(ContextCompat.getColor(context, R.color.white_93))
    .setTextIsHtml(true)
    .setIconDrawable(ContextCompat.getDrawable(context, R.drawable.ic_profile))
    .setBackgroundColor(ContextCompat.getColor(context, R.color.colorPrimary))
    .setOnBalloonClickListener(onBalloonClickListener)
    .setBalloonAnimation(BalloonAnimation.FADE)
    .setLifecycleOwner(lifecycleOwner)
    .build();

Create using kotlin dsl

This is how to create Balloon instance using kotlin dsl.

val balloon = createBalloon(context) {
  setArrowSize(10)
  setWidth(BalloonSizeSpec.WRAP)
  setHeight(65)
  setArrowPosition(0.7f)
  setCornerRadius(4f)
  setAlpha(0.9f)
  setText("You can access your profile from now on.")
  setTextColorResource(R.color.white_93)
  setTextIsHtml(true)
  setIconDrawable(ContextCompat.getDrawable(context, R.drawable.ic_profile))
  setBackgroundColorResource(R.color.colorPrimary)
  setOnBalloonClickListener(onBalloonClickListener)
  setBalloonAnimation(BalloonAnimation.FADE)
  setLifecycleOwner(lifecycleOwner)
}

Width and height

We can handle sizes of the popup width and height using the below ways.
If we would not set any specific sizes of the width/height of the balloon, the size of the popup will be decided by the content.

Padding

Balloon wraps a content. We can control the content size of the balloon using padding of the content.

balloon.setPadding(6) // sets 6dp padding to all directions (left-top-right-bottom)
balloon.setPaddingLeft(8) // sets 8dp padding to content's left.
balloon.setPaddingTop(12) // sets 12dp padding to content's top.

Specific size

We can set the specific size of the balloon regardless size of the contents.

balloon.setWidth(220) // sets 220dp width size.
balloon.setHeight(160) // sets 160dp height size.
balloon.setWidth(BalloonSizeSpec.WRAP) // sets width size depending on the content's size.
balloon.setHeight(BalloonSizeSpec.WRAP) // sets height size depending on the content's size.

According to screen ratio

Also, we can set the width according to the ratio of the horizontal screen's size.

balloon.setWidthRatio(0.5f) // sets width as 50% of the horizontal screen's size.

Show and dismiss

This is how to show balloon popup and dismiss.
We can set the balloon popup's position using x-Offset and y-Offset attributes.
And show based on top/bottom/right/left alignment if we use showAlign__ method.

balloon.show(anchor: View) // shows the balloon on the center of an anchor view.
balloon.show(anchor: View, xOff: Int, yOff: Int) // shows the balloon on an anchor view with x-off and y-off.
balloon.showAlignTop(anchor: View) // shows the balloon on an anchor view as the top alignment.
balloon.showAlignTop(anchor: View, xOff: Int, yOff: Int) // shows top alignment with x-off and y-off.
balloon.showAlignBottom(anchor: View) // shows the balloon on an anchor view as the bottom alignment.
balloon.showAlignBottom(anchor: View, xOff: Int, yOff: Int) // shows bottom alignment with x-off and y-off.
balloon.showAlignRight(anchor: View) // shows the balloon on an anchor view as the right alignment.
balloon.showAlignRight(anchor: View, xOff: Int, yOff: Int) // shows right alignment with x-off and y-off.
balloon.showAlignLeft(anchor: View) // shows the balloon on an anchor view as the left alignment.
balloon.showAlignLeft(anchor: View, xOff: Int, yOff: Int) // shows left alignment with x-off and y-off.

Or we can show balloon popup using kotlin extension.

myButton.showAlignTop(balloon)

We can dismiss popup simply using Balloon.dismiss() method.

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 using
setAutoDismissDuration method on Balloon.Builder.

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

Show sequentially

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.

customListBalloon
  .relayShowAlignBottom(customProfileBalloon, circleImageView) // relay to customListBalloon
  .relayShowAlignTop(customTagBalloon, bottomNavigationView, 130, 0) // relay to customProfileBalloon

// show sequentially customListBalloon-customProfileBalloon-customTagBalloon
customListBalloon.showAlignBottom(toolbar_list)

Margin

If the location of the balloon according to the anchor is located at the boundaries on the screen,
the balloon will be stick to the end of the screen. In that case, we can resolve by giving margins to the balloon.

.setMargin(12) // sets the margin on the balloon all directions.
.setMarginLeft(14) // sets the left margin on the balloon.
.setMarginRight(14) // sets the right margin on the balloon.

Arrow Composition

We can customize the arrow on the balloon popup.

.setIsVisibleArrow(true) // sets the visibility of the arrow.
.setArrowSize(10) // sets the arrow size.
.setArrowSize(BalloonSizeSpec.WRAP) // sets arrow size depending on the original resources' 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(context, R.drawable.arrow)) // sets the arrow drawable.

ArrowPositionRules

We can determine the position of the arrow depending on the aligning rules using the ArrowPositionRules.

// Align the arrow position depending on an anchor.
// if `arrowPosition` is 0.5, the arrow will be located in the middle of an anchor.
.setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR) // default

// Align the arrow position depending on the balloon popup body.
// if `arrowPosition` is 0.5, he arrow will be located in the middle of the tooltip.
.setArrowPositionRules(ArrowPositionRules.ALIGN_BALLOON)

ArrowOrientationRules

We can determine the orientation of the arrow depending on the aligning rules using the ArrowOrientationRules.

// Align depending on the position of an anchor.
// For example, `arrowOrientation` is ArrowOrientation.TOP and 
// we want to show up the balloon under an anchor using the `Balloon.showAlignBottom`.
// However, if there is not enough free space to place the tooltip at the bottom of the anchor,
// tooltips will be placed top of the anchor and the orientation of the arrow will be `ArrowOrientation.BOTTOM`.
.setArrowOrientationRules(ArrowOrientationRules.ALIGN_ANCHOR) // default

// Align to fixed ArrowOrientation value.
.setArrowOrientationRules(ArrowOrientationRules.ALIGN_FIXED)

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.

Orientation: BOTTOM
Position: 0.62
showAlignTop
Orientation: TOP
Position : 0.5
showAlignBottom
Orientation: LEFT
Position: 0.5
showAlignRight
Orientation: RIGHT
Position: 0.5
showAlignLeft

Text Composition

We can customize the text on the balloon popup.

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

If your text has HTML in it, you can enable HTML rendering by adding this:

.setTextIsHtml(true)

This will parse the text using Html.fromHtml(text).

TextForm

TextFrom is an attribute class that has some attributes about TextView for customizing popup text.

TextForm textForm = new TextForm.Builder(context)
  .setText("This is a TextForm")
  .setTextColorResource(R.color.colorPrimary)
  .setTextSize(14f)
  .setTextTypeface(Typeface.BOLD)
  .build();

builder.setTextForm(textForm);

This is how to create TextForm using kotlin dsl.

val form = textForm(context) {
  text = "This is a TextForm"
  textColor = ContextCompat.getColor(context, com.skydoves.balloondemo.R.color.white_87)
  textSize = 14f
  textTypeface = Typeface.BOLD
}

Icon Composition

We can customize the icon on the balloon popup.

.setIconSpace(10) // sets right margin of the icon.
.setIconSize(20) // sets size of the icon.
.setIconDrawable(ContextCompat.getDrawable(context, R.drawable.ic_edit)) // sets a drawable resource.

IconForm

IconForm is an attribute class that has some attributes about ImageView for customizing popup icon.

IconForm iconForm = new IconForm.Builder(context)
  .setDrawable(ContextCompat.getDrawable(context, R.drawable.arrow))
  .setIconColor(ContextCompat.getColor(context, R.color.colorPrimary))
  .setIconSize(20)
  .setIconSpace(12)
  .build();
  
builder.setIconForm(iconForm);

This is how to create IconForm using kotlin dsl.

val form = iconForm(context) {
  drawable = ContextCompat.getDrawable(context, R.drawable.arrow)
  iconColor = ContextCompat.getColor(context, R.color.skyblue)
  iconSize = 20
  iconSpace = 12
}

OnBalloonClickListener, OnBalloonDismissListener, OnBalloonOutsideTouchListener

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

balloon.setOnBalloonClickListener(new OnBalloonClickListener() {
  @Override
  public void onBalloonClick() {
    // doSomething;
  }
});
    
balloon.setOnBalloonDismissListener(new OnBalloonDismissListener() {
  @Override
  public void onBalloonDismiss() {
    // doSomething;
  }
});

balloon.setOnBalloonOutsideTouchListener(new OnBalloonOutsideTouchListener() {
  @Override
  public void onBalloonOutsideTouch() {
    // doSomething;
  }
});

We can simplify it using kotlin.

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

Customized layout

We can fully customize the balloon layout using below method.

.setLayout(R.layout.my_balloon_layout)

This is an example of implementing custom balloon popup.

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

val balloon = Balloon.Builder(context)
      .setLayout(R.layout.layout_custom_profile)
      .setArrowSize(10)
      .setArrowOrientation(ArrowOrientation.TOP)
      .setArrowPosition(0.5f)
      .setWidthRatio(0.55f)
      .setHeight(250)
      .setCornerRadius(4f)
      .setBackgroundColor(ContextCompat.getColor(this, R.color.black))
      .setBalloonAnimation(BalloonAnimation.CIRCULAR)
      .setLifecycleOwner(lifecycleOwner)
      .build()

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(context, "Edit", Toast.LENGTH_SHORT).show()
  balloon.dismiss()
}

Persistence

If you want to show-up the balloon popup only once or a specific number of times, here is how to implement it simply.

.setPreferenceName("MyBalloon") // sets preference name of the Balloon.
.setShowCounts(3) // show-up three of times the popup. the default value is 1.
.runIfReachedShowCounts {
  // do something after the preference showing counts is reached the goal.
}

But you can implement it more variously using Only.

only("introPopup", times = 3) {
  onDo { balloon.showAlignTop(anchor) }
}

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, Fragment, and View.

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>()

  // ...
}

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

ProfileBalloonFactory.kt

class ProfileBalloonFactory : Balloon.Factory() {

  override fun create(context: Context, lifecycle: LifecycleOwner): Balloon {
    return createBalloon(context) {
      setLayout(R.layout.layout_custom_profile)
      setArrowSize(10)
      setArrowOrientation(ArrowOrientation.TOP)
      setArrowPosition(0.5f)
      setWidthRatio(0.55f)
      setHeight(250)
      setCornerRadius(4f)
      setBackgroundColor(ContextCompat.getColor(context, R.color.background900))
      setBalloonAnimation(BalloonAnimation.CIRCULAR)
      setLifecycleOwner(lifecycle)
    }
  }
}

BalloonOverlay

We can show an overlay window over the whole screen except an anchor view.

.setIsVisibleOverlay(true) // sets the visibility of the overlay for highlighting an anchor.
.setOverlayColorResource(R.color.overlay) // background color of the overlay using a color resource.
.setOverlayPadding(6f) // sets a padding value of the overlay shape internally.
.setBalloonOverlayAnimation(BalloonOverlayAnimation.FADE) // default is fade.
.setDismissWhenOverlayClicked(false) // disable dismissing the balloon when the overlay is clicked.

We can change the shape of the highlighting using .setOverlayShape.

.setOverlayShape(BalloonOverlayOval) // default shape
.setOverlayShape(BalloonOverlayRect)
.setOverlayShape(BalloonOverlayCircle(radius = 36f))
.setOverlayShape(BalloonOverlayRoundRect(12f, 12f))
OVAL CIRCLE RECT ROUNDRECT

And we can set the specific position of the overlay shape using the below method.

.setOverlayPosition(Point(x, y)) // sets a specific position of the overlay shape.

BalloonAnimation

We can implement popup animations when showing and dismissing.

BalloonAnimation.NONE
BalloonAnimation.FADE
BalloonAnimation.OVERSHOOT
BalloonAnimation.ELASTIC
BalloonAnimation.CIRCULAR
FADE OVERSHOOT ELASTIC CIRCULAR

Balloon builder methods

We can reference all kinds and descriptions of functions details here.

.setWidth(value: Int)
.setWidthRatio(@FloatRange(from = 0.0, to = 1.0) value: Float)
.setHeight(value: Int)
.setSize(value: Int, value: Int)
.setSpace(value: Int)
.setPadding(value: Int)
.setPaddingLeft(value: Int)
.setPaddingTop(value: Int)
.setPaddingRight(value: Int)
.setPaddingBottom(value: Int)
.setMargin(value: Int)
.setMarginLeft(value: Int)
.setMarginTop(value: Int)
.setMarginRight(value: Int)
.setMarginBottom(value: Int)
.setElevation(value: Int)
.setIsVisibleArrow(value: Boolean)
.setArrowSize(value: Int)
.setArrowPosition(@FloatRange(from = 0.0, to = 1.0) value: Float)
.setArrowOrientation(value: ArrowOrientation)
.setArrowPositionRules(ArrowPositionRules.ALIGN_ANCHOR)
.setArrowColor(value: Int)
.setArrowColorResource(@ColorRes value: Int)
.setArrowDrawable(value: Drawable?)
.setArrowDrawableResource(@DrawableRes value: Int)
.setArrowAlignAnchorPadding(value: Int)
.setArrowAlignAnchorPaddingRatio(value: Float)
.setBackgroundColor(value: Int)
.setBackgroundColorResource(@ColorRes value: Int)
.setBackgroundDrawable(value: Drawable?)
.setBackgroundDrawableResource(@DrawableRes value: Int)
.setCornerRadius(value: Float)
.setText(value: String)
.setTextResource(value: Int)
.setTextColor(value: Int)
.setTextColorResource(value: Int)
.setTextSize(value: Float)
.setTextTypeface(value: Int)
.setTextGravity(value: Int)
.setTextForm(value: TextForm)
.setIconDrawable(value: Drawable?)
.setIconDrawableResource(@DrawableRes value: Int)
.setIconSize(value: Int)
.setIconWidth(value: Int)
.setIconHeight(value: Int)
.setIconColor(value: Int)
.setIconColorResource(@ColorRes value: Int)
.setIconSpace(value: Int)
.setIconForm(value: IconForm)
.setIconGravity(value: IconGravity)
.setAlpha(@FloatRange(from = 0.0, to = 1.0) value: Float)
.setLayout(@LayoutRes layout: Int)
.setIsVisibleOverlay(value: Boolean)
.setOverlayColor(@ColorInt value: Int)
.setOverlayColorResource(@ColorRes value: Int)
.setOverlayPadding(@Dp value: Float)
.setOverlayPosition(value: Point)
.setOverlayShape(value: BalloonOverlayShape)
.setPreferenceName(value: String)
.setShowCount(value: Int)
.setFocusable(value: Boolean)
.setLifecycleOwner(value: LifecycleOwner)
.setDismissWhenClicked(value: Boolean)
.setDismissWhenLifecycleOnPause(value: Boolean)
.setDismissWhenTouchOutside(value: Boolean)
.setDismissWhenShowAgain(value: Boolean)
.setDismissWhenOverlayClicked(value: Boolean)
.setBalloonAnimation(value: BalloonAnimation)
.setOnBalloonClickListener(value: OnBalloonClickListener)
.setOnBalloonDismissListener(value: OnBalloonDismissListener)
.setOnBalloonInitializedListener(value: OnBalloonInitializedListener)
.setOnBalloonOutsideTouchListener(value: OnBalloonOutsideTouchListener)
.setOnBalloonOverlayClickListener(value: OnBalloonOverlayClickListener)
.setDismissWhenTouchOutside(value: Boolean)

Find this library useful? ❤️

Support it by joining stargazers for this repository.

License

Copyright 2019 skydoves (Jaewoong Eum)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

GitHub

https://github.com/skydoves/balloon
Comments
  • 1. Balloon does not adapt to the height of a custom View

    Please complete the following information:

    • Library version: com.github.skydoves:balloon:1.2.7
    • Affected Device: Samsung Galaxy s9 with Android 9.0

    Describe the Bug:

    We have implemented Balloon with a custom layout. Although the height has specified, some of the text will be cut out if the user adjusts the system font-size.

    Code: https://github.com/wikimedia/apps-android-wikipedia/blob/22b07cdefc8fd184601ae657a2b01dcefd31ed57/app/src/main/java/org/wikipedia/util/FeedbackUtil.java#L226-L234

    Expected Behavior: The library adapts the height of the custom view automatically.

    I have checked https://github.com/skydoves/Balloon/issues/112 but it does not work for me.

    Reviewed by cooltey at 2021-01-26 22:37
  • 2. ClassCastException: android.widget.RelativeLayout cannot be cast to android.widget.FrameLayout

    • Library-Version - 1.2.5
    • Affected Device(s) Google Pixel 2XL OS - Android 11

    The crash occurs when casting this,

    java.lang.ClassCastException: android.widget.RelativeLayout cannot be cast to android.widget.FrameLayout at com.skydoves.balloon.databinding.LayoutBalloonBinding.bind(LayoutBalloonBinding.java:82) at com.skydoves.balloon.databinding.LayoutBalloonBinding.inflate(LayoutBalloonBinding.java:73) at com.skydoves.balloon.Balloon.(Balloon.kt:97) at com.skydoves.balloon.Balloon$Builder.build(Balloon.kt:1677)

    Material Version Used - 1.1.0 LifeCycle Version - 2.3.0-beta01

    I was using 1.1.9 before this. After updating to 1.2.0+ versions the above crash appears

    Reviewed by MujammilAhamed at 2020-11-12 12:08
  • 3. balloon content always has radius..

    happened in Latest version

    just create a list as balloon content ,then add android:background="?attr/selectableItemBackground" on list item for click background status

    however, when i click first item or last item ..radius limit ripple background

    that not expected .. only wanna radius on balloon not for content

    Reviewed by asker517 at 2021-07-29 06:41
  • 4. Width of the balloon with a custom layout not adjusted properly when setting text dynamically

    Please complete the following information:

    • Library Version 1.3.1+
    • Affected Device(s) All devices, in particular, Pixel 3a API 28

    Describe the Bug: We are using Balloon.Factory() with a custom layout resource that includes several textViews. After setting text values dynamically, the width of the balloon is not resized to wrap its content (it is fixed with the initial value). As a result, the height of the content increases. The solution provided in #91 cannot be used anymore, since method measureTextWidth is private now. The issue can be easily reproduced using the demo application:

    Step 1 - Add id to the TextView in the layout_custom_tag.xml:

      <TextView
        android:id="@+id/tagTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="12"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        android:textStyle="bold"
        tools:ignore="HardcodedText" />
    

    Step 2 - change the text of that tagTextView to a different value, i.e.:

    customTagBalloon.getContentView().findViewById<TextView>(R.id.tagTextView).text = "Long text"
    

    Step 3 - launch the application and click on the bottomNavBar. As a result, the height of the balloon increased, instead of the width. However, it works properly on version 1.3.0 of the library. Modified demo app to reproduce the issue is also available at: https://github.com/wkubaty/Balloon/tree/custom-layout-size-issue

    Unexpected behavior on version 1.3.4:

    Zrzut ekranu 2021-05-25 o 14 47 00

    Expected Behavior: The width of the balloon with a custom layout should be adjusted properly when setting content dynamically.

    Proper behavior on version 1.3.0:

    Zrzut ekranu 2021-05-25 o 14 48 45
    Reviewed by wkubaty at 2021-05-25 13:33
  • 5. Balloon does not adapt to the height of a custom View on certain devices

    Please complete the following information:

    • Library Version 1.3.3
    • Affected Device(s) Pixel 3 XL with Android 11 and Pixel 2 with Android 10

    Describe the Bug:

    Addressed here: https://github.com/skydoves/Balloon/issues/148#issuecomment-805262834

    The previous fix works fine on Samsung S9 with Android 10, but not on Pixel 3 XL and Pixel 2 and possibly some other devices.

    Expected Behavior:

    Content should not be cutoff.

    Reviewed by cooltey at 2021-03-23 21:16
  • 6. Fatal Exception: java.lang.IllegalStateException

    Please complete the following information:

    • Library Version 1.2.9
    • Affected Device(s) Samsung, Xiaomi, HUAWEI, OPPO OS 8,9,10

    The Bug:

    I'm using only showAlignTop method, but in some cases this crash is popping on my Firebase Crashlytics.

    The specified child already has a parent. You must call removeView() on the child's parent first.

    Fatal Exception: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
           at android.view.ViewGroup.addViewInner(ViewGroup.java:5106)
           at android.view.ViewGroup.addView(ViewGroup.java:4935)
           at android.view.ViewGroup.addView(ViewGroup.java:4907)
           at android.widget.PopupWindow.createBackgroundView(PopupWindow.java:1530)
           at android.widget.PopupWindow.preparePopup(PopupWindow.java:1492)
           at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1417)
           at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1380)
           at com.skydoves.balloon.Balloon$showAlignTop$$inlined$show$1.run(Balloon.java:1774)
           at android.os.Handler.handleCallback(Handler.java:883)
           at android.os.Handler.dispatchMessage(Handler.java:100)
           at android.os.Looper.loop(Looper.java:214)
           at android.app.ActivityThread.main(ActivityThread.java:7403)
           at java.lang.reflect.Method.invoke(Method.java)
           at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
    

    Possible solution:

    Was not able to full reproduce yet, but maybe we can find the right part of the library and to some parent check, like

    if(tv.getParent() != null) {
        ((ViewGroup)tv.getParent()).removeView(tv); // <- fix
    }
    layout.addView(tv);
    
    Reviewed by Canato at 2021-01-27 11:39
  • 7. Balloon crashes on showing, BadTokenException

    We are using the newest Balloon version 1.1.5 in our app.

    I just saw a crash in crashlytics and cannot reproduce it on my own, but it occurred 9 times until now.

    Android-Versions: no specific version

    Stacktrace: Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? at android.view.ViewRootImpl.setView(ViewRootImpl.java:1122) at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:450) at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:95) at android.widget.PopupWindow.invokePopup(PopupWindow.java:1621) at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1456) at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1412) at com.skydoves.balloon.Balloon$showAlignBottom$$inlined$show$2.run(Balloon.java:970) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:237) at android.app.ActivityThread.main(ActivityThread.java:8016) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076) at com.android.internal.os.Device.fp(Device.java)

    Could you at least catch this exception, so it won't crash the app? Thank you very much!

    Reviewed by dpeters-ipc at 2020-05-07 09:22
  • 8. Unable to build balloon object

    Please complete the following information:

    • Library Version [e.g. v1.2..3]
    • Affected Device(s) [e.g. One Plus 6 Android 10, Nexus S Android 8 Emulator]

    Describe the Bug:

    Add a clear description about the problem. On tap of an icon i want to show tooltip but on build I am getting: java.lang.NullPointerException: Missing required view with ID: balloon Expected Behavior:

    A clear description of what you expected to happen: I want to show the tooltip

    Reviewed by dduvedi at 2020-09-22 20:47
  • 9. Bug setWidth(BalloonSizeSpec.WRAP) v 1.4.5

    • Library Version 1.4.5
    • Affected Device(s) All devices

    I have a custom view that I use with balloon, the width WRAP not working correctly. I am displaying the view in a item from tabLayout. I tried using the container linearLayout, relativeLayout and constraintLayout but I can't get it to fit the content. The width of the textView are dynamic. The only way for the width to change is by setting it to a fixed value

    tabLayout.getTabAt(TAB_NOTIFICATIONS_POSITION)?.view?.let { val nUnreadLikes = 100L val nUnreadComments = 1232L val nUnreadFollowers = 12000L Balloon.Builder(it.context) .setLayout(R.layout.popup_window_notifications) .setArrowOrientation(ArrowOrientation.TOP) .setArrowPosition(0.5f) .setHeight(BalloonSizeSpec.WRAP) .setWidth(BalloonSizeSpec.WRAP) .setCornerRadius(4f) .setArrowColorMatchBalloon(true) .setBackgroundDrawable(ContextCompat.getDrawable(it.context, R.drawable.bg_popup_window_notification)) .setBalloonAnimation(BalloonAnimation.FADE) .setLifecycleOwner(lifecycleOwner) .setAutoDismissDuration(4000) .setOnBalloonInitializedListener { view -> val tvLikes = view.findViewById(R.id.tvLikes) val tvComments = view.findViewById(R.id.tvComments) val tvFollowers = view.findViewById(R.id.tvFollowers) nUnreadLikes.let { tvLikes?.text = it.abbreviateNumber() } nUnreadComments.let { tvComments?.text = it.abbreviateNumber() } nUnreadFollowers.let { tvFollowers?.text = it.abbreviateNumber() } }.build().showAlignTop(it)

    XML resource

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvLikes"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:textColor="@color/White"
        android:fontFamily="@font/lato_medium"
        android:textSize="13sp"
        android:maxLines="1"
        android:paddingEnd="16dp"
        android:paddingStart="0dp"
        android:layout_alignParentStart="true"
        app:drawableStartCompat="@drawable/ic_heart_notifications"
        tools:text="100" />
    
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvComments"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:textColor="@color/White"
        android:fontFamily="@font/lato_medium"
        android:textSize="13sp"
        android:maxLines="1"
        android:layout_toEndOf="@+id/tvLikes"
        app:drawableStartCompat="@drawable/ic_comments_notifications"
        android:paddingEnd="16dp"
        android:paddingStart="0dp"
        tools:text="1.232" />
    
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvFollowers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawablePadding="4dp"
        android:textColor="@color/White"
        android:fontFamily="@font/lato_medium"
        android:textSize="13sp"
        android:layout_toEndOf="@+id/tvComments"
        android:maxLines="1"
        app:drawableStartCompat="@drawable/ic_profile_notifications"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@+id/tvComments"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="12K" />
    

    Parent container RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="10dp"

    Current Captura de Pantalla 2022-05-04 a la(s) 16 57 49

    Expected Captura de Pantalla 2022-05-04 a la(s) 16 59 37

    Please help me! thank you!

    Reviewed by gonzalomonzon02 at 2022-05-04 20:00
  • 10. App crash when calling showAlignBottom

    • Affected Device(s) [Android 11,10,9,8]

    Fatal Exception: java.lang.NullPointerException Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference

    com.skydoves.balloon.Balloon$showAlignBottom$$inlined$show$1.run Same code working great with showAlignTop

    If you want I can share Firebase Crashlytics logs.

    Piyush Sinha Testbook.com

    Reviewed by PiyushSinha-tb at 2021-05-26 07:48
  • 11. After Dismiss First Popup Another is Not Open without Second Time Click

    Please complete the following information:

    • Library Version [e.g. v1.2.6]
    • Affected Device(s) [e.g. Samsung Galaxy J7 Pro with Android 9.0]

    Describe the Bug:

    • I used This Lib in Bottom Navigation.when I click on any bottom navigation Tab it is open.when I click on second tab first popup dismiss but not open second one.After Click Second Time New PopUp open.please help me for same. BottomPopUP

    Expected Behavior: -I want when i click on tab that particular tab popup open and old one dismiss if it is open.

    Reviewed by shreyushah at 2020-11-25 05:12
  • 12. setShouldPassTouchEventToAnchor doesn't pass touch event to anchor

    Please complete the following information:

    • Library Version: 1.4.0
    • Affected Device(s): Pixel 6 Pro, Android 12

    Describe the Bug:

    When setting setShouldPassTouchEventToAnchor(true), no touch event is passed to the anchor view. Specifically, within private fun passTouchEventToAnchor(anchor: View), the setOnBalloonOverlayTouchListener listener is never called https://github.com/skydoves/Balloon/blob/857882d58e3ee4e7bdb00faa4b25c848d810da0c/balloon/src/main/kotlin/com/skydoves/balloon/Balloon.kt#L1285

    Expected Behavior: If calling setShouldPassTouchEventToAnchor(true), all touch events should be passed to the anchor view so that the anchor view properly receives a touch event.

    Reviewed by TylerMcCraw at 2021-12-14 20:19
  • 13. Align to Anchor showAlignTop/showAlignBottom not executing during orientation change

    • Library Version - v1.3.4
    • Affected Device- e.g. Redmi note 9 pro max with Android 11.0

    The balloon is not respecting showAlignTop/showAlignBottom . This is happening only for the first time during orientation change and not subsequent times.

    balloon = createBalloon(this) {
            setIsVisibleArrow(true)
            setArrowSize(BalloonSizeSpec.WRAP)
            setArrowPosition(0.5f)
            setArrowOrientation(if (isLandscape([email protected])) ArrowOrientation.LEFT else 
            ArrowOrientation.BOTTOM)
            setArrowPositionRules(ArrowPositionRules.ALIGN_BALLOON)
            setArrowDrawable(
                ContextCompat.getDrawable(
                    [email protected],
                    R.drawable.xyz
                )
            )
            
            setLayout(R.layout.abc)
            setMargin(6)
            setCornerRadius(15f)
            setBackgroundColorResource(R.color.grey)
            setBalloonAnimation(BalloonAnimation.CIRCULAR)
            setLifecycleOwner(lifecycleOwner)
            setIsVisibleOverlay(true) // sets the visibility of the overlay for highlighting an anchor.
            setOverlayColorResource(R.color.overlay) // background color of the overlay using a color resource.
            setOverlayPadding(6f) // sets a padding value of the overlay shape internally.
            setOverlayShape(BalloonOverlayRoundRect(15f, 15f))
            setBalloonOverlayAnimation(BalloonOverlayAnimation.FADE) // default is fade.
            setDismissWhenOverlayClicked(false)
        }
    
    
     if (isLandscape(this)) {
            (landscape layout anchor)?.let { balloon.showAlignRight(it) }
        } else {
            (portrait layout anchor)?.let { balloon.showAlignTop(it) }
        }
    
        balloon.setOnBalloonOutsideTouchListener { _, _ ->
            viewmodel.setTooltipClicked(true)
        }
    

    `

    https://user-images.githubusercontent.com/87669932/137732947-5748da17-b5e3-46f2-a8fc-11a559c64d0c.mp4

    D561AC48-39F6-4F92-B0A1-8BF405CBE038_1_105_c

    It should respect the "align to anchor" code

    BFA9634B-B857-43BD-BF63-FEC839267FAC_1_105_c

    Reviewed by RahulPilot at 2021-10-18 12:44
  • 14. Whole balloon is focused when displayed initially

    • Library Version 1.3.9
    • Affected Device(s): I've tested it on devices with API 30 only, so Pixels and the Emulator

    Describe the Bug:

    I have implemented your library as a tool for showing mentions. The problem is, that after I show the pop-up, it gains focus I assume, which disappears after you touch the pop-up.

    This is how I'm using the library:

    balloon = Balloon.Builder(anchor.context)
                    .setLayout(popupLayout)
                    .setArrowSize(14)
                    .setWidthRatio(0.72f)
                    .setArrowElevation(4)
                    .setFocusable(false)
                    .setBackgroundColorResource(R.color.white)
                    .setElevation(4)
                    .setLifecycleOwner(anchor.findViewTreeLifecycleOwner())
                    .build()
    

    Also, I tried using .setOverlayShape(BalloonOverlayCircle(radius = 0f)), but it didn't change anything.

    Expected Behavior:

    I would expect to have the pop-up show without that tint or focus or whatever it is. Any idea what might be causing the tint to show like that?

    Reviewed by nvasilev0101 at 2021-10-04 18:52
  • 15. Manage horizontal and vertical size of arrow

    Is your feature request related to a problem?

    I would like to make an arrow like the picture below. But i didn't find solution. I think the size of arrow prevent.

    image-20210805-151353

    Describe the solution you'd like:

    I would like to have 2 properties : setArrowVerticalSize(@Dp value: Int) setArrowHorizontalSize(@Dp value: Int)

    Reviewed by spvince at 2021-08-06 08:44
  • 16. Suggestion for ballon property?

    Is your feature request related to a problem?

    Not sure if this already exists but, looking for the arrow properties i didn't see something that could let me implement this

    Screen Shot 2021-08-02 at 4 21 33 PM

    The idea would be, instead of an arrow, to be able to add a border to highlight the source object.

    Describe the solution you'd like:

    Could be a new BallonProperty, something like:

    setShouldHighlightOrigin(Boolean) // Default false
    setHighligthStrokeWidth(Int) // Border stroke width
    setHighligthStrokeColor(Int) // Border color
    
    Reviewed by alejandro-rios at 2021-08-02 21:45
  • 17. Stoke color to Balloon and Arrow

    Hi , and thanks for thiis useful library, I try to create a simple balloon with white background , arrow, and black stroke color. Is it possible to have a stroke outline to balloon? Alternatively, is it possible to create a shadow to simulate a stroke?

    thanks in advance

    Reviewed by JohnGeorgiou at 2020-04-14 12:15
Attractive, stylish and customizable toast library for Android.

FabToast min SDK 16 (Jelly Bean 4.1) written in Java To download the demo app for this library from Google Playstore so you can see it in action, clic

Feb 14, 2022
Pretty material design toasts with feedback animations
Pretty material design toasts with feedback animations

Load Toast Library The default toasts are ugly and don't really provide much more than a short message. This small library provides a better toast whi

May 10, 2022
Toastie is a customizable Android toast library.
Toastie is a customizable Android toast library.

Toastie Getting Started Gradle Step 1. Add the JitPack repository to your build file Add it in your root build.gradle at the end of repositories: Note

Apr 3, 2021
Customizable toast message library for Android
Customizable toast message library for Android

Android Custom Toast Message (SnToast) Customizable Toast Message Library For Android Add this in your root build.gradle file allprojects { reposito

Nov 15, 2021
In-layout notifications. Based on Toast notifications and article by Cyril Mottier (http://android.cyrilmottier.com/?p=773).
In-layout notifications. Based on Toast notifications and article by Cyril Mottier (http://android.cyrilmottier.com/?p=773).

Android AppMsg (Crouton) Library Implementation of in-layout notifications. Based on Toast notifications and article The making of Prixing #4: in-layo

May 19, 2022
Custom toasts with color and icon for Android.
Custom toasts with color and icon for Android.

Dynamic Toasts A simple library to display themed toasts with icon and text on Android 2.3 (API 9) and above devices. Since v0.4.0, it uses 26.x.x sup

May 10, 2022
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

May 22, 2022
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

Apr 27, 2021
Balloon 🎈 A lightweight popup like tooltips, fully customizable with arrow and animations.
Balloon  🎈 A lightweight popup like tooltips, fully customizable with arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations.

May 20, 2022
PowerSpinner - 🌀 A lightweight dropdown popup spinner, fully customizable with an arrow and animations for Android.
PowerSpinner - 🌀 A lightweight dropdown popup spinner, fully customizable with an arrow and animations for Android.

PowerSpinner - ?? A lightweight dropdown popup spinner, fully customizable with an arrow and animations for Android.

May 18, 2022
Arrow-Maven-Template - A template project for Arrow with Maven. It defines an application and a couple small examples

Arrow-Maven-Template A template project for Arrow on Maven. See the official doc

Jan 11, 2022
Space Navigation is a library allowing easily integrate fully customizable Google Spaces like navigation to your app.
Space Navigation is a library allowing easily integrate fully customizable Google Spaces like navigation to your app.

Space-Navigation-View Introduction Space Navigation is a library allowing easily integrate fully customizable Google [Spaces][1] like navigation to yo

May 8, 2022
Tapping-Game - Following the random instruction one has to tap the correct ball or balloon with correct text on it

Tapping-Game A simple game where a player has to follow the random instruction a

Jan 18, 2022
Nested popup menus with smooth height animations
Nested popup menus with smooth height animations

cascade cascade builds nested popup menus with smooth height animations. It is designed to be a drop-in replacement for PopupMenu so using it in your

May 13, 2022
Android-NewPopupMenu 3.9 0.0 Java is an android library to create popup menu with GoogleMusic app-like style.
Android-NewPopupMenu 3.9 0.0 Java  is an android library to create popup menu with GoogleMusic app-like style.

Android-NewPopupMenu Android-NewPopupMenu is an android library to create popup menu with GoogleMusic app-like style. Requirements Tested with APIv4 H

Jul 4, 2020
A lightweight android library that allows to you create custom fast forward/rewind animations like on Netflix.
A lightweight android library that allows to you create custom fast forward/rewind animations like on Netflix.

SuperForwardView About A lightweight android library that allows to you create custom fast forward/rewind animations like on Netflix. GIF Design Credi

Mar 25, 2022
A small and simple, yet fully fledged and customizable navigation library for Jetpack Compose
A small and simple, yet fully fledged and customizable navigation library for Jetpack Compose

A small and simple, yet fully fledged and customizable navigation library for Jetpack Compose

May 20, 2022
📅 CosmoCalendar is a fully customizable calendar with a wide variety of features and displaying modes.
📅 CosmoCalendar is a fully customizable calendar with a wide variety of features and displaying modes.

CosmoCalendar Made by Applikey Solutions Usage Customization Common Selection Current day Navigation buttons Weekend days Connected days Disabled days

May 3, 2022