Skip to content

Commit a295de9

Browse files
hunterstichleticiarossi
authored andcommitted
[TabLayout] Added fade inidcator animation mode.
PiperOrigin-RevId: 417842993
1 parent dac9bf3 commit a295de9

File tree

5 files changed

+85
-7
lines changed

5 files changed

+85
-7
lines changed

lib/java/com/google/android/material/tabs/ElasticTabIndicatorInterpolator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private static float accInterp(@FloatRange(from = 0.0, to = 1.0) float fraction)
4444
}
4545

4646
@Override
47-
void setIndicatorBoundsForOffset(
47+
void updateIndicatorForOffset(
4848
TabLayout tabLayout,
4949
View startTitle,
5050
View endTitle,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (C) 2021 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.android.material.tabs;
18+
19+
import static com.google.android.material.animation.AnimationUtils.lerp;
20+
21+
import android.graphics.RectF;
22+
import android.graphics.drawable.Drawable;
23+
import android.view.View;
24+
import androidx.annotation.NonNull;
25+
26+
/**
27+
* An implementation of {@link TabIndicatorInterpolator} that sequentially fades out the selected
28+
* tab indicator from the current destination and fades it back in at its new destination.
29+
*/
30+
class FadeTabIndicatorInterpolator extends TabIndicatorInterpolator {
31+
32+
// When the indicator will disappear from the current tab and begin to reappear at the newly
33+
// selected tab.
34+
private static final float FADE_THRESHOLD = 0.5F;
35+
36+
@Override
37+
void updateIndicatorForOffset(
38+
TabLayout tabLayout,
39+
View startTitle,
40+
View endTitle,
41+
float offset,
42+
@NonNull Drawable indicator) {
43+
View tab = offset < FADE_THRESHOLD ? startTitle : endTitle;
44+
RectF bounds = calculateIndicatorWidthForTab(tabLayout, tab);
45+
float alpha = offset < FADE_THRESHOLD
46+
? lerp(1F, 0F, 0F, FADE_THRESHOLD, offset)
47+
: lerp(0F, 1F, FADE_THRESHOLD, 1F, offset);
48+
49+
indicator.setBounds(
50+
(int) bounds.left,
51+
indicator.getBounds().top,
52+
(int) bounds.right,
53+
indicator.getBounds().bottom
54+
);
55+
indicator.setAlpha((int) (alpha * 255F));
56+
}
57+
}

lib/java/com/google/android/material/tabs/TabIndicatorInterpolator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
* TabLayout#isTabIndicatorFullWidth()} and linearly move the indicator between tabs.
3838
*
3939
* <p>Subclasses can override {@link #setIndicatorBoundsForTab(TabLayout, View, Drawable)} and
40-
* {@link #setIndicatorBoundsForOffset(TabLayout, View, View, float, Drawable)} (TabLayout, View,
41-
* View, float, Drawable)} to define how the indicator should be drawn for a single tab or at any
42-
* point between two tabs.
40+
* {@link #updateIndicatorForOffset(TabLayout, View, View, float, Drawable)} (TabLayout, View, View,
41+
* float, Drawable)} to define how the indicator should be drawn for a single tab or at any point
42+
* between two tabs.
4343
*
4444
* <p>Additionally, subclasses can use the provided helpers {@link
4545
* #calculateIndicatorWidthForTab(TabLayout, View)} and {@link
@@ -152,7 +152,7 @@ void setIndicatorBoundsForTab(TabLayout tabLayout, View tab, @NonNull Drawable i
152152
* @param indicator The drawable to be drawn to indicate the selected tab. Update the drawable's
153153
* bounds, color, etc as {@code offset} changes to show the indicator in the correct position.
154154
*/
155-
void setIndicatorBoundsForOffset(
155+
void updateIndicatorForOffset(
156156
TabLayout tabLayout,
157157
View startTitle,
158158
View endTitle,

lib/java/com/google/android/material/tabs/TabLayout.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,23 @@ public class TabLayout extends HorizontalScrollView {
383383
*/
384384
public static final int INDICATOR_ANIMATION_MODE_ELASTIC = 1;
385385

386+
/**
387+
* Indicator animation mode used to switch the selected tab indicator from one tab to another
388+
* by sequentially fading it out from the current destination and in at its new destination.
389+
*
390+
* @see #setTabIndicatorAnimationMode(int)
391+
* @see #getTabIndicatorAnimationMode()
392+
* @attr ref com.google.android.material.R.styleable#TabLayout_tabIndicatorAnimationMode
393+
*/
394+
public static final int INDICATOR_ANIMATION_MODE_FADE = 2;
395+
386396
/** @hide */
387397
@RestrictTo(LIBRARY_GROUP)
388-
@IntDef(value = {INDICATOR_ANIMATION_MODE_LINEAR, INDICATOR_ANIMATION_MODE_ELASTIC})
398+
@IntDef(value = {
399+
INDICATOR_ANIMATION_MODE_LINEAR,
400+
INDICATOR_ANIMATION_MODE_ELASTIC,
401+
INDICATOR_ANIMATION_MODE_FADE
402+
})
389403
@Retention(RetentionPolicy.SOURCE)
390404
public @interface TabIndicatorAnimationMode {}
391405

@@ -1071,6 +1085,9 @@ public void setTabIndicatorAnimationMode(
10711085
case INDICATOR_ANIMATION_MODE_ELASTIC:
10721086
this.tabIndicatorInterpolator = new ElasticTabIndicatorInterpolator();
10731087
break;
1088+
case INDICATOR_ANIMATION_MODE_FADE:
1089+
this.tabIndicatorInterpolator = new FadeTabIndicatorInterpolator();
1090+
break;
10741091
default:
10751092
throw new IllegalArgumentException(
10761093
tabIndicatorAnimationMode + " is not a valid TabIndicatorAnimationMode");
@@ -3136,7 +3153,7 @@ private void jumpIndicatorToSelectedPosition() {
31363153
private void tweenIndicatorPosition(View startTitle, View endTitle, float fraction) {
31373154
boolean hasVisibleTitle = startTitle != null && startTitle.getWidth() > 0;
31383155
if (hasVisibleTitle) {
3139-
tabIndicatorInterpolator.setIndicatorBoundsForOffset(
3156+
tabIndicatorInterpolator.updateIndicatorForOffset(
31403157
TabLayout.this, startTitle, endTitle, fraction, tabSelectedIndicator);
31413158
} else {
31423159
// Hide the indicator by setting the drawable's width to 0 and off screen.

lib/java/com/google/android/material/tabs/res/values/attrs.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
This causes the indicator to look like it stretches between destinations
6767
an then shrinks back down to fit the size of it's target tab. -->
6868
<enum name="elastic" value="1"/>
69+
<!-- Animate the selection indicator by sequentially fading it out from
70+
its current destination and then fading it in at its new
71+
destination. -->
72+
<enum name="fade" value="2"/>
6973
</attr>
7074
<!-- The behavior mode for the Tabs in this layout -->
7175
<attr name="tabMode">

0 commit comments

Comments
 (0)