Skip to content

Commit

Permalink
[TabLayout] Added fade inidcator animation mode.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 417842993
  • Loading branch information
hunterstich authored and leticiarossi committed Dec 22, 2021
1 parent dac9bf3 commit a295de9
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private static float accInterp(@FloatRange(from = 0.0, to = 1.0) float fraction)
}

@Override
void setIndicatorBoundsForOffset(
void updateIndicatorForOffset(
TabLayout tabLayout,
View startTitle,
View endTitle,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* 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.
*/

package com.google.android.material.tabs;

import static com.google.android.material.animation.AnimationUtils.lerp;

import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.NonNull;

/**
* An implementation of {@link TabIndicatorInterpolator} that sequentially fades out the selected
* tab indicator from the current destination and fades it back in at its new destination.
*/
class FadeTabIndicatorInterpolator extends TabIndicatorInterpolator {

// When the indicator will disappear from the current tab and begin to reappear at the newly
// selected tab.
private static final float FADE_THRESHOLD = 0.5F;

@Override
void updateIndicatorForOffset(
TabLayout tabLayout,
View startTitle,
View endTitle,
float offset,
@NonNull Drawable indicator) {
View tab = offset < FADE_THRESHOLD ? startTitle : endTitle;
RectF bounds = calculateIndicatorWidthForTab(tabLayout, tab);
float alpha = offset < FADE_THRESHOLD
? lerp(1F, 0F, 0F, FADE_THRESHOLD, offset)
: lerp(0F, 1F, FADE_THRESHOLD, 1F, offset);

indicator.setBounds(
(int) bounds.left,
indicator.getBounds().top,
(int) bounds.right,
indicator.getBounds().bottom
);
indicator.setAlpha((int) (alpha * 255F));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
* TabLayout#isTabIndicatorFullWidth()} and linearly move the indicator between tabs.
*
* <p>Subclasses can override {@link #setIndicatorBoundsForTab(TabLayout, View, Drawable)} and
* {@link #setIndicatorBoundsForOffset(TabLayout, View, View, float, Drawable)} (TabLayout, View,
* View, float, Drawable)} to define how the indicator should be drawn for a single tab or at any
* point between two tabs.
* {@link #updateIndicatorForOffset(TabLayout, View, View, float, Drawable)} (TabLayout, View, View,
* float, Drawable)} to define how the indicator should be drawn for a single tab or at any point
* between two tabs.
*
* <p>Additionally, subclasses can use the provided helpers {@link
* #calculateIndicatorWidthForTab(TabLayout, View)} and {@link
Expand Down Expand Up @@ -152,7 +152,7 @@ void setIndicatorBoundsForTab(TabLayout tabLayout, View tab, @NonNull Drawable i
* @param indicator The drawable to be drawn to indicate the selected tab. Update the drawable's
* bounds, color, etc as {@code offset} changes to show the indicator in the correct position.
*/
void setIndicatorBoundsForOffset(
void updateIndicatorForOffset(
TabLayout tabLayout,
View startTitle,
View endTitle,
Expand Down
21 changes: 19 additions & 2 deletions lib/java/com/google/android/material/tabs/TabLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,23 @@ public class TabLayout extends HorizontalScrollView {
*/
public static final int INDICATOR_ANIMATION_MODE_ELASTIC = 1;

/**
* Indicator animation mode used to switch the selected tab indicator from one tab to another
* by sequentially fading it out from the current destination and in at its new destination.
*
* @see #setTabIndicatorAnimationMode(int)
* @see #getTabIndicatorAnimationMode()
* @attr ref com.google.android.material.R.styleable#TabLayout_tabIndicatorAnimationMode
*/
public static final int INDICATOR_ANIMATION_MODE_FADE = 2;

/** @hide */
@RestrictTo(LIBRARY_GROUP)
@IntDef(value = {INDICATOR_ANIMATION_MODE_LINEAR, INDICATOR_ANIMATION_MODE_ELASTIC})
@IntDef(value = {
INDICATOR_ANIMATION_MODE_LINEAR,
INDICATOR_ANIMATION_MODE_ELASTIC,
INDICATOR_ANIMATION_MODE_FADE
})
@Retention(RetentionPolicy.SOURCE)
public @interface TabIndicatorAnimationMode {}

Expand Down Expand Up @@ -1071,6 +1085,9 @@ public void setTabIndicatorAnimationMode(
case INDICATOR_ANIMATION_MODE_ELASTIC:
this.tabIndicatorInterpolator = new ElasticTabIndicatorInterpolator();
break;
case INDICATOR_ANIMATION_MODE_FADE:
this.tabIndicatorInterpolator = new FadeTabIndicatorInterpolator();
break;
default:
throw new IllegalArgumentException(
tabIndicatorAnimationMode + " is not a valid TabIndicatorAnimationMode");
Expand Down Expand Up @@ -3136,7 +3153,7 @@ private void jumpIndicatorToSelectedPosition() {
private void tweenIndicatorPosition(View startTitle, View endTitle, float fraction) {
boolean hasVisibleTitle = startTitle != null && startTitle.getWidth() > 0;
if (hasVisibleTitle) {
tabIndicatorInterpolator.setIndicatorBoundsForOffset(
tabIndicatorInterpolator.updateIndicatorForOffset(
TabLayout.this, startTitle, endTitle, fraction, tabSelectedIndicator);
} else {
// Hide the indicator by setting the drawable's width to 0 and off screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@
This causes the indicator to look like it stretches between destinations
an then shrinks back down to fit the size of it's target tab. -->
<enum name="elastic" value="1"/>
<!-- Animate the selection indicator by sequentially fading it out from
its current destination and then fading it in at its new
destination. -->
<enum name="fade" value="2"/>
</attr>
<!-- The behavior mode for the Tabs in this layout -->
<attr name="tabMode">
Expand Down

0 comments on commit a295de9

Please sign in to comment.