nambicompany / expandable-fab

A highly customizable 'speed dial' FloatingActionButton implementation.
https://nambicompany.github.io/expandable-fab/
MIT License
199 stars 20 forks source link

Add attributes for changing how fast the FabOptions appear and disappear #43

Closed TacoTheDank closed 2 years ago

TacoTheDank commented 2 years ago

See how fast the FabOptions appear in the README .gif example of this other FAB menu library.

Notice how slow the expandable-fab library's FabOptions appear in the README .gif examples by comparison.

Is there an attribute that allows me to control how fast the FabOptions appear and disappear? I would like to make it faster like in the other FAB menu library.

kabumere commented 2 years ago

Hey @TacoTheDank,

Yes, the library provides APIs for changing the speeds (we call them "animation durations") of every component within the widget. So you can adjust the appearing speed ("opening animation duration") and disappearing speed (closing animation duration") of every component that animates, like the Overlay, FabOption, Label etc. Which set of APIs you use depends on how granular you need to be.

You can either:

1) Adjust the speed of every component individually, right on the component itself as mentioned on this previous ticket. So say you have an ExpandableFab widget with 5 FabOptions and 5 Labels. The set of API calls above will allow you to give different appearing and disappearing speeds to each individual FabOption and each individual Label.

2) Or you can adjust the speeds of components by type right on the ExpandableFabLayout as seen starting here (these properties are also shown on the JavaDoc and KDoc on the project website). So with this set of API calls, if you had an ExpandableFab widget with 5 FabOptions and 5 Labels, then all 5 FabOptions would share whatever speed you set for the FabOption type, and all 5 Labels would share the speed you set for the Label type.

So again, it depends on how granular you need to be. Most users will probably be better off using the "global" API settings mentioned in option 2.

Please let me know if you have any questions when using the APIs.

TacoTheDank commented 2 years ago

The trouble I was having was with the names of the attributes and their documentation in the library code. Some of it is rather ambiguous in places, leading me to have to figure out what they actually did through testing. Some of the attributes I think could have better names for distinguishment.

Thanks for clarifying! I will attempt this tomorrow.

kabumere commented 2 years ago

Hey @TacoTheDank,

My thinking of the placement of the APIs were:

1) The animations are being done on (and exposed through) the individual components, so I made each component hold it's own properties for controlling the length of said animation. 2) But seeing as how changing each components' individual animation properties could get repetitive if you have many components in your layout, I also included global animation duration properties on the ExpandableFabLayout (considering the ExpandableFabLayout is the parent ViewGroup of the widget, this placement made sense as the holder of "global" properties that affect all its children).

As for the naming, you "open" and "close" the ExpandableFab with its FabOptions, and the animations are played during this opening and closing, so it seemed right for the naming of the properties to be "openingAnimationDuration" (the duration of this component's opening animation) and "closingAnimationDuration" (the duration of this component's closing animation).

The only component that does not follow this naming scheme is the Label, and that's because the ExpandableFab itself can have a Label that's shown when it is closed (unlike the FabOption which shows its label only when it is opened). Because of this difference, Label's use the naming hiddenToVisibleAnimationDuration (the duration of the label's animation when it's going from a hidden state to a visible state) and visibleToHiddenAnimationDuration (the duration of the label's animation when it's going from a visible to hidden state).

And for all the properties above, they have extensive documentation that details all of this (JavaDoc, KDoc and listed on the code itself).

For example, the documentation for FabOption's openingAnimationDurationMs property states:

The duration (in milliseconds as a positive long) of the animations that will be played when this FabOption is being shown from a hidden state (when the ExpandableFab is opening). Set to 0L if you don't want opening animations played. Default value is 125L.

I felt this pretty clearly stated what that property was used for in an unambiguous manner.

All this isn't to say the library is perfect, just to explain that naming, placement of API and documentation wasn't done arbitrarily. I welcome suggestions on better names or placement of properties though, as I want the library to be intuitive to users. What was intuitive to me at creation may very well not be intuitive to others.

TacoTheDank commented 2 years ago

Thanks for the explanation.

So what I found was that the fab_openingAnimationDurationMs attribute affects the speed of each FabOption's pop-in animation, and how fast they show up in succession to one another is merely a side effect of that. However, each FabOption animation still has to complete before the next FabOption can be animated into view. The bad thing about this is that if you set it to something like 10, the FabOption appearance animation is so quick that you can't even see the actual animation (it appears to just pop in out of nowhere).

What I am looking for is a way to control the amount of time between each FabOption appearing independently of how fast each FabOption's pop-in animation is. I want to make the animations appear more simultaneously instead of one after the other. I would like to control the amount of time between each FabOption appearing, not how fast each FabOption appears individually.

I don't know if I'm explaining it well. Let me know if you would like examples.

kabumere commented 2 years ago

Hey @TacoTheDank,

Yes you're explaining it correctly. Unfortunately what you're experiencing is a side effect of how the library is designed.

There are a number of factors that go into how a FabOption is drawn on the screen.

  1. The FabOption's "at rest size" is dictated by FabSize which could be MINI, NORMAL, AUTO or a CUSTOM size altogether.

  2. I put "at rest size" in quotes above, because we also expose an API for allowing a FabOption to animate past its regular "at rest size" during its opening animation before shrinking back down to its "at rest size" using openingOvershootTension.

  3. Developers can also control the margin between FabOptions using properties firstFabOptionMarginPx and successiveFabOptionMarginPx.

  4. And finally as we're discussing, openingAnimationDurationMs represents how long it takes for a FabOption to complete all it's animating and drawing onto the screen.

So taking into account all the customizations listed above: in order to get each FabOption to align perfectly centered above the one that proceeds it when they're both finished drawing, I make the previous FabOption the anchor of the next FabOption being drawn. So each FabOption necessarily depends on the previous one being fully drawn and "at rest" before it can draw itself.

For instance, imagine the following environment:

If fab1 and fab2 began drawing themselves on screen and animating simultaneously, then fab1 would take 5 times as long as fab2 to draw. During this time, fab1's size is also rapidly changing (in fact, because of the openingOvershootTension value, fab1 would potentially draw over fab2 at some point before shrinking back down). If fab2 wanted to center itself above fab1 (while respecting the margin set by successiveFabOptionMarginPx!), but it's drawing is to be done 5 times faster, how would that work?

Now, maybe there are calculations we could do to predetermine the final resting space fab1 would occupy, and then use this value to have fab2 correctly draw itself independent of fab1, but this would 1) start getting complicated, and 2) look off to end users in practice. fab2 would be finished drawing and waiting seemingly away from the action while the first FabOption was still dancing about.

EDIT: For what it's worth, I suspect that the other FAB library you mentioned probably also finishes drawing the preceding FAB before moving to the next one as well. It just sets it at such a fast speed that they look like they're appearing independently. Hopefully if you choose to keep using the ExpandableFab library you're able to play around with values to mimic that quick-open animation.

TacoTheDank commented 2 years ago

I understand now, thanks for the great explanation.

kabumere commented 2 years ago

@TacoTheDank no problem, and thanks for making me reconsider my design choices. If there's anything you see that we can fix/update later, let me know. Always like making projects better.