mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-16 01:42:16 +08:00
2235 lines
80 KiB
Java
2235 lines
80 KiB
Java
/*
|
|
* Copyright (C) 2015 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.appbar;
|
|
|
|
import com.google.android.material.R;
|
|
|
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
|
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;
|
|
|
|
import android.animation.TimeInterpolator;
|
|
import android.animation.ValueAnimator;
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Rect;
|
|
import android.graphics.Region.Op;
|
|
import android.graphics.Typeface;
|
|
import android.graphics.drawable.ColorDrawable;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build.VERSION;
|
|
import android.os.Build.VERSION_CODES;
|
|
import android.text.TextUtils;
|
|
import android.text.TextUtils.TruncateAt;
|
|
import android.util.AttributeSet;
|
|
import android.view.Gravity;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewParent;
|
|
import android.widget.FrameLayout;
|
|
import androidx.annotation.ColorInt;
|
|
import androidx.annotation.DrawableRes;
|
|
import androidx.annotation.FloatRange;
|
|
import androidx.annotation.IntDef;
|
|
import androidx.annotation.IntRange;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresApi;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.annotation.StyleRes;
|
|
import androidx.core.graphics.drawable.DrawableCompat;
|
|
import androidx.core.math.MathUtils;
|
|
import androidx.core.util.ObjectsCompat;
|
|
import androidx.core.view.ViewCompat;
|
|
import androidx.core.view.WindowInsetsCompat;
|
|
import com.google.android.material.animation.AnimationUtils;
|
|
import com.google.android.material.color.MaterialColors;
|
|
import com.google.android.material.elevation.ElevationOverlayProvider;
|
|
import com.google.android.material.internal.CollapsingTextHelper;
|
|
import com.google.android.material.internal.DescendantOffsetUtils;
|
|
import com.google.android.material.internal.ThemeEnforcement;
|
|
import com.google.android.material.motion.MotionUtils;
|
|
import com.google.android.material.resources.MaterialResources;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
|
|
/**
|
|
* CollapsingToolbarLayout is a wrapper for {@code Toolbar} which implements a collapsing app bar.
|
|
* It is designed to be used as a direct child of a {@link AppBarLayout}. CollapsingToolbarLayout
|
|
* contains the following features:
|
|
*
|
|
* <h4>Collapsing title</h4>
|
|
*
|
|
* A title which is larger when the layout is fully visible but collapses and becomes smaller as the
|
|
* layout is scrolled off screen. You can set the title to display via {@link
|
|
* #setTitle(CharSequence)}. The title appearance can be tweaked via the {@code
|
|
* collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
|
|
*
|
|
* <h4>Content scrim</h4>
|
|
*
|
|
* A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
|
|
* You can change this via {@link #setContentScrim(Drawable)}.
|
|
*
|
|
* <h4>Status bar scrim</h4>
|
|
*
|
|
* A scrim which is shown or hidden behind the status bar when the scroll position has hit a certain
|
|
* threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works on
|
|
* {@link android.os.Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system
|
|
* windows.
|
|
*
|
|
* <h4>Parallax scrolling children</h4>
|
|
*
|
|
* Child views can opt to be scrolled within this layout in a parallax fashion. See {@link
|
|
* LayoutParams#COLLAPSE_MODE_PARALLAX} and {@link LayoutParams#setParallaxMultiplier(float)}.
|
|
*
|
|
* <h4>Pinned position children</h4>
|
|
*
|
|
* Child views can opt to be pinned in space globally. This is useful when implementing a collapsing
|
|
* as it allows the {@code Toolbar} to be fixed in place even though this layout is moving. See
|
|
* {@link LayoutParams#COLLAPSE_MODE_PIN}.
|
|
*
|
|
* <p><strong>Do not manually add views to the Toolbar at run time</strong>. We will add a 'dummy
|
|
* view' to the Toolbar which allows us to work out the available space for the title. This can
|
|
* interfere with any views which you add.
|
|
*
|
|
* <p>For more information, see the <a
|
|
* href="https://github.com/material-components/material-components-android/blob/master/docs/components/TopAppBar.md">component
|
|
* developer guidance</a> and <a href="https://material.io/components/top-app-bar/overview">design
|
|
* guidelines</a>.
|
|
*
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_contentScrim
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMargin
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_statusBarScrim
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_toolbarId
|
|
*/
|
|
public class CollapsingToolbarLayout extends FrameLayout {
|
|
|
|
private static final int DEF_STYLE_RES = R.style.Widget_Design_CollapsingToolbar;
|
|
private static final int DEFAULT_SCRIM_ANIMATION_DURATION = 600;
|
|
|
|
/**
|
|
* The expanded title will continuously scale and translate to its final collapsed position.
|
|
*
|
|
* @see #setTitleCollapseMode(int)
|
|
* @see #getTitleCollapseMode()
|
|
*/
|
|
public static final int TITLE_COLLAPSE_MODE_SCALE = 0;
|
|
|
|
/**
|
|
* The expanded title will fade out and translate, and the collapsed title will fade in.
|
|
*
|
|
* @see #setTitleCollapseMode(int)
|
|
* @see #getTitleCollapseMode()
|
|
*/
|
|
public static final int TITLE_COLLAPSE_MODE_FADE = 1;
|
|
|
|
/** @hide */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@IntDef(value = {TITLE_COLLAPSE_MODE_SCALE, TITLE_COLLAPSE_MODE_FADE})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface TitleCollapseMode {}
|
|
|
|
/**
|
|
* The gravity of the collapsed title is based on the entire space of the CollapsedToolbarLayout.
|
|
*/
|
|
private static final int COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE = 0;
|
|
|
|
/**
|
|
* The gravity of the collapsed title is based on the remaining space in the
|
|
* CollapsedToolbarLayout after accounting for other views such as the menu.
|
|
*/
|
|
private static final int COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE = 1;
|
|
|
|
/**
|
|
* The mode in which to calculate the gravity of the collapsed title.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@IntDef(value = {COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE, COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface CollapsedTitleGravityMode {}
|
|
|
|
private boolean refreshToolbar = true;
|
|
private int toolbarId;
|
|
@Nullable private ViewGroup toolbar;
|
|
@Nullable private View toolbarDirectChild;
|
|
private View dummyView;
|
|
|
|
private int expandedMarginStart;
|
|
private int expandedMarginTop;
|
|
private int expandedMarginEnd;
|
|
private int expandedMarginBottom;
|
|
private int expandedTitleSpacing;
|
|
|
|
private final Rect tmpRect = new Rect();
|
|
@NonNull final CollapsingTextHelper collapsingTitleHelper;
|
|
@NonNull final CollapsingTextHelper collapsingSubtitleHelper;
|
|
@NonNull final ElevationOverlayProvider elevationOverlayProvider;
|
|
private boolean collapsingTitleEnabled;
|
|
private boolean drawCollapsingTitle;
|
|
@CollapsedTitleGravityMode private final int collapsedTitleGravityMode;
|
|
|
|
@Nullable private Drawable contentScrim;
|
|
@Nullable Drawable statusBarScrim;
|
|
private int scrimAlpha;
|
|
private boolean scrimsAreShown;
|
|
private ValueAnimator scrimAnimator;
|
|
private long scrimAnimationDuration;
|
|
private final TimeInterpolator scrimAnimationFadeInInterpolator;
|
|
private final TimeInterpolator scrimAnimationFadeOutInterpolator;
|
|
private int scrimVisibleHeightTrigger = -1;
|
|
|
|
private AppBarLayout.OnOffsetChangedListener onOffsetChangedListener;
|
|
|
|
int currentOffset;
|
|
|
|
private int screenOrientation;
|
|
|
|
@TitleCollapseMode private int titleCollapseMode;
|
|
|
|
@Nullable WindowInsetsCompat lastInsets;
|
|
private int topInsetApplied = 0;
|
|
private boolean forceApplySystemWindowInsetTop;
|
|
|
|
private int extraMultilineTitleHeight = 0;
|
|
private int extraMultilineSubtitleHeight = 0;
|
|
private boolean extraMultilineHeightEnabled;
|
|
|
|
private int extraHeightForTitles = 0;
|
|
|
|
public CollapsingToolbarLayout(@NonNull Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public CollapsingToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
|
|
this(context, attrs, R.attr.collapsingToolbarLayoutStyle);
|
|
}
|
|
|
|
public CollapsingToolbarLayout(
|
|
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr);
|
|
// Ensure we are using the correctly themed context rather than the context that was passed in.
|
|
context = getContext();
|
|
|
|
screenOrientation = getResources().getConfiguration().orientation;
|
|
|
|
collapsingTitleHelper = new CollapsingTextHelper(this);
|
|
collapsingTitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
|
|
collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(false);
|
|
|
|
elevationOverlayProvider = new ElevationOverlayProvider(context);
|
|
|
|
TypedArray a =
|
|
ThemeEnforcement.obtainStyledAttributes(
|
|
context, attrs, R.styleable.CollapsingToolbarLayout, defStyleAttr, DEF_STYLE_RES);
|
|
|
|
int titleExpandedGravity =
|
|
a.getInt(
|
|
R.styleable.CollapsingToolbarLayout_expandedTitleGravity,
|
|
Gravity.START | Gravity.BOTTOM);
|
|
int titleCollapsedGravity =
|
|
a.getInt(
|
|
R.styleable.CollapsingToolbarLayout_collapsedTitleGravity,
|
|
Gravity.START | Gravity.CENTER_VERTICAL);
|
|
collapsedTitleGravityMode =
|
|
a.getInt(
|
|
R.styleable.CollapsingToolbarLayout_collapsedTitleGravityMode,
|
|
COLLAPSED_TITLE_GRAVITY_AVAILABLE_SPACE);
|
|
|
|
collapsingTitleHelper.setExpandedTextGravity(titleExpandedGravity);
|
|
collapsingTitleHelper.setCollapsedTextGravity(titleCollapsedGravity);
|
|
|
|
expandedMarginStart =
|
|
expandedMarginTop =
|
|
expandedMarginEnd =
|
|
expandedMarginBottom =
|
|
a.getDimensionPixelSize(
|
|
R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0);
|
|
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart)) {
|
|
expandedMarginStart =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginStart, 0);
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd)) {
|
|
expandedMarginEnd =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginEnd, 0);
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop)) {
|
|
expandedMarginTop =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop, 0);
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom)) {
|
|
expandedMarginBottom =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0);
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing)) {
|
|
expandedTitleSpacing =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleSpacing, 0);
|
|
}
|
|
|
|
collapsingTitleEnabled = a.getBoolean(R.styleable.CollapsingToolbarLayout_titleEnabled, true);
|
|
setTitle(a.getText(R.styleable.CollapsingToolbarLayout_title));
|
|
|
|
// First load the default text appearances
|
|
collapsingTitleHelper.setExpandedTextAppearance(
|
|
R.style.TextAppearance_Design_CollapsingToolbar_Expanded);
|
|
collapsingTitleHelper.setCollapsedTextAppearance(
|
|
androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Title);
|
|
|
|
// Now overlay any custom text appearances
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance)) {
|
|
collapsingTitleHelper.setExpandedTextAppearance(
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedTitleTextAppearance, 0));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance)) {
|
|
collapsingTitleHelper.setCollapsedTextAppearance(
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedTitleTextAppearance, 0));
|
|
}
|
|
|
|
// Now overlay any custom text Ellipsize
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_titleTextEllipsize)) {
|
|
setTitleEllipsize(
|
|
convertEllipsizeToTruncateAt(
|
|
a.getInt(R.styleable.CollapsingToolbarLayout_titleTextEllipsize, -1)));
|
|
}
|
|
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleTextColor)) {
|
|
collapsingTitleHelper.setExpandedTextColor(
|
|
MaterialResources.getColorStateList(
|
|
context, a, R.styleable.CollapsingToolbarLayout_expandedTitleTextColor));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor)) {
|
|
collapsingTitleHelper.setCollapsedTextColor(
|
|
MaterialResources.getColorStateList(
|
|
context, a, R.styleable.CollapsingToolbarLayout_collapsedTitleTextColor));
|
|
}
|
|
|
|
scrimVisibleHeightTrigger =
|
|
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_scrimVisibleHeightTrigger, -1);
|
|
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_titleMaxLines)) {
|
|
collapsingTitleHelper.setExpandedMaxLines(
|
|
a.getInt(R.styleable.CollapsingToolbarLayout_titleMaxLines, 1));
|
|
} else if (a.hasValue(R.styleable.CollapsingToolbarLayout_maxLines)) {
|
|
collapsingTitleHelper.setExpandedMaxLines(
|
|
a.getInt(R.styleable.CollapsingToolbarLayout_maxLines, 1));
|
|
}
|
|
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) {
|
|
collapsingTitleHelper.setPositionInterpolator(
|
|
android.view.animation.AnimationUtils.loadInterpolator(
|
|
context,
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0)));
|
|
}
|
|
|
|
collapsingSubtitleHelper = new CollapsingTextHelper(this);
|
|
collapsingSubtitleHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
|
|
collapsingSubtitleHelper.setRtlTextDirectionHeuristicsEnabled(false);
|
|
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_subtitle)) {
|
|
setSubtitle(a.getText(R.styleable.CollapsingToolbarLayout_subtitle));
|
|
}
|
|
|
|
collapsingSubtitleHelper.setExpandedTextGravity(titleExpandedGravity);
|
|
collapsingSubtitleHelper.setCollapsedTextGravity(titleCollapsedGravity);
|
|
collapsingSubtitleHelper.setExpandedTextAppearance(
|
|
androidx.appcompat.R.style.TextAppearance_AppCompat_Headline);
|
|
collapsingSubtitleHelper.setCollapsedTextAppearance(
|
|
androidx.appcompat.R.style.TextAppearance_AppCompat_Widget_ActionBar_Subtitle);
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance)) {
|
|
collapsingSubtitleHelper.setExpandedTextAppearance(
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextAppearance, 0));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance)) {
|
|
collapsingSubtitleHelper.setCollapsedTextAppearance(
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextAppearance, 0));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor)) {
|
|
collapsingSubtitleHelper.setExpandedTextColor(
|
|
MaterialResources.getColorStateList(
|
|
context, a, R.styleable.CollapsingToolbarLayout_expandedSubtitleTextColor));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor)) {
|
|
collapsingSubtitleHelper.setCollapsedTextColor(
|
|
MaterialResources.getColorStateList(
|
|
context, a, R.styleable.CollapsingToolbarLayout_collapsedSubtitleTextColor));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_subtitleMaxLines)) {
|
|
collapsingSubtitleHelper.setExpandedMaxLines(
|
|
a.getInt(R.styleable.CollapsingToolbarLayout_subtitleMaxLines, 1));
|
|
}
|
|
if (a.hasValue(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator)) {
|
|
collapsingSubtitleHelper.setPositionInterpolator(
|
|
android.view.animation.AnimationUtils.loadInterpolator(
|
|
context,
|
|
a.getResourceId(R.styleable.CollapsingToolbarLayout_titlePositionInterpolator, 0)));
|
|
}
|
|
|
|
scrimAnimationDuration =
|
|
a.getInt(
|
|
R.styleable.CollapsingToolbarLayout_scrimAnimationDuration,
|
|
DEFAULT_SCRIM_ANIMATION_DURATION);
|
|
scrimAnimationFadeInInterpolator =
|
|
MotionUtils.resolveThemeInterpolator(
|
|
context,
|
|
R.attr.motionEasingStandardInterpolator,
|
|
AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
|
|
scrimAnimationFadeOutInterpolator =
|
|
MotionUtils.resolveThemeInterpolator(
|
|
context,
|
|
R.attr.motionEasingStandardInterpolator,
|
|
AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
|
|
|
|
setContentScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_contentScrim));
|
|
setStatusBarScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_statusBarScrim));
|
|
|
|
setTitleCollapseMode(
|
|
a.getInt(R.styleable.CollapsingToolbarLayout_titleCollapseMode, TITLE_COLLAPSE_MODE_SCALE));
|
|
|
|
toolbarId = a.getResourceId(R.styleable.CollapsingToolbarLayout_toolbarId, -1);
|
|
|
|
forceApplySystemWindowInsetTop =
|
|
a.getBoolean(R.styleable.CollapsingToolbarLayout_forceApplySystemWindowInsetTop, false);
|
|
|
|
extraMultilineHeightEnabled =
|
|
a.getBoolean(R.styleable.CollapsingToolbarLayout_extraMultilineHeightEnabled, false);
|
|
|
|
a.recycle();
|
|
|
|
setWillNotDraw(false);
|
|
|
|
ViewCompat.setOnApplyWindowInsetsListener(
|
|
this,
|
|
new androidx.core.view.OnApplyWindowInsetsListener() {
|
|
@Override
|
|
public WindowInsetsCompat onApplyWindowInsets(
|
|
View v, @NonNull WindowInsetsCompat insets) {
|
|
return onWindowInsetChanged(insets);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
protected void onAttachedToWindow() {
|
|
super.onAttachedToWindow();
|
|
|
|
// Add an OnOffsetChangedListener if possible
|
|
final ViewParent parent = getParent();
|
|
if (parent instanceof AppBarLayout) {
|
|
AppBarLayout appBarLayout = (AppBarLayout) parent;
|
|
|
|
disableLiftOnScrollIfNeeded(appBarLayout);
|
|
|
|
// Copy over from the ABL whether we should fit system windows
|
|
setFitsSystemWindows(appBarLayout.getFitsSystemWindows());
|
|
|
|
if (onOffsetChangedListener == null) {
|
|
onOffsetChangedListener = new OffsetUpdateListener();
|
|
}
|
|
appBarLayout.addOnOffsetChangedListener(onOffsetChangedListener);
|
|
|
|
// We're attached, so lets request an inset dispatch
|
|
requestApplyInsets();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onDetachedFromWindow() {
|
|
// Remove our OnOffsetChangedListener if possible and it exists
|
|
final ViewParent parent = getParent();
|
|
if (onOffsetChangedListener != null && parent instanceof AppBarLayout) {
|
|
((AppBarLayout) parent).removeOnOffsetChangedListener(onOffsetChangedListener);
|
|
}
|
|
|
|
super.onDetachedFromWindow();
|
|
}
|
|
|
|
WindowInsetsCompat onWindowInsetChanged(@NonNull final WindowInsetsCompat insets) {
|
|
WindowInsetsCompat newInsets = null;
|
|
|
|
if (getFitsSystemWindows()) {
|
|
// If we're set to fit system windows, keep the insets
|
|
newInsets = insets;
|
|
}
|
|
|
|
// If our insets have changed, keep them and invalidate the scroll ranges...
|
|
if (!ObjectsCompat.equals(lastInsets, newInsets)) {
|
|
lastInsets = newInsets;
|
|
requestLayout();
|
|
}
|
|
|
|
// Consume the insets. This is done so that child views with fitSystemWindows=true do not
|
|
// get the default padding functionality from View
|
|
return insets.consumeSystemWindowInsets();
|
|
}
|
|
|
|
@Override
|
|
public void draw(@NonNull Canvas canvas) {
|
|
super.draw(canvas);
|
|
|
|
// If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
|
|
// Instead, we draw it here, before our collapsing text.
|
|
ensureToolbar();
|
|
if (toolbar == null && contentScrim != null && scrimAlpha > 0) {
|
|
contentScrim.mutate().setAlpha(scrimAlpha);
|
|
contentScrim.draw(canvas);
|
|
}
|
|
|
|
// Let the collapsing text helper draw its text
|
|
if (collapsingTitleEnabled && drawCollapsingTitle) {
|
|
if (toolbar != null
|
|
&& contentScrim != null
|
|
&& scrimAlpha > 0
|
|
&& isTitleCollapseFadeMode()
|
|
&& collapsingTitleHelper.getExpansionFraction()
|
|
< collapsingTitleHelper.getFadeModeThresholdFraction()) {
|
|
// Mask the expanded text with the contentScrim
|
|
int save = canvas.save();
|
|
canvas.clipRect(contentScrim.getBounds(), Op.DIFFERENCE);
|
|
collapsingTitleHelper.draw(canvas);
|
|
collapsingSubtitleHelper.draw(canvas);
|
|
canvas.restoreToCount(save);
|
|
} else {
|
|
collapsingTitleHelper.draw(canvas);
|
|
collapsingSubtitleHelper.draw(canvas);
|
|
}
|
|
}
|
|
|
|
// Now draw the status bar scrim
|
|
if (statusBarScrim != null && scrimAlpha > 0) {
|
|
final int topInset = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;
|
|
if (topInset > 0) {
|
|
statusBarScrim.setBounds(0, -currentOffset, getWidth(), topInset - currentOffset);
|
|
statusBarScrim.mutate().setAlpha(scrimAlpha);
|
|
statusBarScrim.draw(canvas);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onConfigurationChanged(@NonNull Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
collapsingTitleHelper.maybeUpdateFontWeightAdjustment(newConfig);
|
|
|
|
// When the orientation changes with extra multiline height enabled and when collapsed, there
|
|
// can be an issue where the offset/scroll state is invalid due to the number of lines of text
|
|
// changing which causes a different height for the collapsing toolbar. We can use a pending
|
|
// action of collapsed to make sure that the collapsing toolbar stays fully collapsed if it was
|
|
// fully collapsed prior to screen rotation.
|
|
if (screenOrientation != newConfig.orientation
|
|
&& extraMultilineHeightEnabled
|
|
&& collapsingTitleHelper.getExpansionFraction() == 1f) {
|
|
ViewParent parent = getParent();
|
|
if (parent instanceof AppBarLayout) {
|
|
AppBarLayout appBarLayout = (AppBarLayout) parent;
|
|
if (appBarLayout.getPendingAction() == AppBarLayout.PENDING_ACTION_NONE) {
|
|
appBarLayout.setPendingAction(AppBarLayout.PENDING_ACTION_COLLAPSED);
|
|
}
|
|
}
|
|
}
|
|
|
|
screenOrientation = newConfig.orientation;
|
|
}
|
|
|
|
@Override
|
|
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
|
// This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),
|
|
// but in front of any other children which are behind it. To do this we intercept the
|
|
// drawChild() call, and draw our scrim just before the Toolbar is drawn
|
|
boolean invalidated = false;
|
|
if (contentScrim != null && scrimAlpha > 0 && isToolbarChild(child)) {
|
|
updateContentScrimBounds(contentScrim, child, getWidth(), getHeight());
|
|
contentScrim.mutate().setAlpha(scrimAlpha);
|
|
contentScrim.draw(canvas);
|
|
invalidated = true;
|
|
}
|
|
return super.drawChild(canvas, child, drawingTime) || invalidated;
|
|
}
|
|
|
|
@Override
|
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
super.onSizeChanged(w, h, oldw, oldh);
|
|
if (contentScrim != null) {
|
|
updateContentScrimBounds(contentScrim, w, h);
|
|
}
|
|
}
|
|
|
|
private boolean isTitleCollapseFadeMode() {
|
|
return titleCollapseMode == TITLE_COLLAPSE_MODE_FADE;
|
|
}
|
|
|
|
private void disableLiftOnScrollIfNeeded(AppBarLayout appBarLayout) {
|
|
// Disable lift on scroll if using fade title collapse mode, since the content scrim can
|
|
// conflict with the lift on scroll color fill.
|
|
if (isTitleCollapseFadeMode()) {
|
|
appBarLayout.setLiftOnScroll(false);
|
|
}
|
|
}
|
|
|
|
private void updateContentScrimBounds(@NonNull Drawable contentScrim, int width, int height) {
|
|
updateContentScrimBounds(contentScrim, this.toolbar, width, height);
|
|
}
|
|
|
|
private void updateContentScrimBounds(
|
|
@NonNull Drawable contentScrim, @Nullable View toolbar, int width, int height) {
|
|
// If using fade title collapse mode and we have a toolbar with a collapsing title, use the
|
|
// toolbar's bottom edge for the scrim so the collapsing title appears to go under the toolbar.
|
|
int bottom =
|
|
isTitleCollapseFadeMode() && toolbar != null && collapsingTitleEnabled
|
|
? toolbar.getBottom()
|
|
: height;
|
|
contentScrim.setBounds(0, 0, width, bottom);
|
|
}
|
|
|
|
private void ensureToolbar() {
|
|
if (!refreshToolbar) {
|
|
return;
|
|
}
|
|
|
|
// First clear out the current Toolbar
|
|
this.toolbar = null;
|
|
toolbarDirectChild = null;
|
|
|
|
if (toolbarId != -1) {
|
|
// If we have an ID set, try and find it and it's direct parent to us
|
|
this.toolbar = findViewById(toolbarId);
|
|
if (this.toolbar != null) {
|
|
toolbarDirectChild = findDirectChild(this.toolbar);
|
|
}
|
|
}
|
|
|
|
if (this.toolbar == null) {
|
|
// If we don't have an ID, or couldn't find a Toolbar with the correct ID, try and find
|
|
// one from our direct children
|
|
ViewGroup toolbar = null;
|
|
for (int i = 0, count = getChildCount(); i < count; i++) {
|
|
final View child = getChildAt(i);
|
|
if (isToolbar(child)) {
|
|
toolbar = (ViewGroup) child;
|
|
break;
|
|
}
|
|
}
|
|
this.toolbar = toolbar;
|
|
}
|
|
|
|
updateDummyView();
|
|
refreshToolbar = false;
|
|
}
|
|
|
|
private static boolean isToolbar(View view) {
|
|
return view instanceof androidx.appcompat.widget.Toolbar
|
|
|| view instanceof android.widget.Toolbar;
|
|
}
|
|
|
|
private boolean isToolbarChild(View child) {
|
|
return (toolbarDirectChild == null || toolbarDirectChild == this)
|
|
? child == toolbar
|
|
: child == toolbarDirectChild;
|
|
}
|
|
|
|
/** Returns the direct child of this layout, which itself is the ancestor of the given view. */
|
|
@NonNull
|
|
private View findDirectChild(@NonNull final View descendant) {
|
|
View directChild = descendant;
|
|
for (ViewParent p = descendant.getParent(); p != this && p != null; p = p.getParent()) {
|
|
if (p instanceof View) {
|
|
directChild = (View) p;
|
|
}
|
|
}
|
|
return directChild;
|
|
}
|
|
|
|
private void updateDummyView() {
|
|
if (!collapsingTitleEnabled && dummyView != null) {
|
|
// If we have a dummy view and we have our title disabled, remove it from its parent
|
|
final ViewParent parent = dummyView.getParent();
|
|
if (parent instanceof ViewGroup) {
|
|
((ViewGroup) parent).removeView(dummyView);
|
|
}
|
|
}
|
|
if (collapsingTitleEnabled && toolbar != null) {
|
|
if (dummyView == null) {
|
|
dummyView = new View(getContext());
|
|
}
|
|
if (dummyView.getParent() == null) {
|
|
toolbar.addView(dummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
ensureToolbar();
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
final int mode = MeasureSpec.getMode(heightMeasureSpec);
|
|
final int topInset = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;
|
|
if ((mode == MeasureSpec.UNSPECIFIED || forceApplySystemWindowInsetTop) && topInset > 0) {
|
|
// If we have a top inset and we're set to wrap_content height or force apply,
|
|
// we need to make sure we add the top inset to our height, therefore we re-measure
|
|
topInsetApplied = topInset;
|
|
int newHeight = getMeasuredHeight() + topInset;
|
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
|
|
updateTitleFromToolbarIfNeeded();
|
|
|
|
if (collapsingTitleEnabled && !TextUtils.isEmpty(collapsingTitleHelper.getText())) {
|
|
final int originalHeight = getMeasuredHeight();
|
|
// Need to update title and bounds in order to calculate line count and text height.
|
|
updateTextBounds(0, 0, getMeasuredWidth(), originalHeight, /* forceRecalculate= */ true);
|
|
|
|
// Calculates the extra height needed for the contents of the collapsing toolbar, if needed.
|
|
int expectedHeight =
|
|
(int)
|
|
(topInsetApplied
|
|
+ expandedMarginTop
|
|
+ collapsingTitleHelper.getExpandedTextFullSingleLineHeight()
|
|
+ (TextUtils.isEmpty(collapsingSubtitleHelper.getText())
|
|
? 0
|
|
: expandedTitleSpacing
|
|
+ collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight())
|
|
+ expandedMarginBottom);
|
|
if (expectedHeight > originalHeight) {
|
|
extraHeightForTitles = expectedHeight - originalHeight;
|
|
} else {
|
|
extraHeightForTitles = 0;
|
|
}
|
|
|
|
if (extraMultilineHeightEnabled) {
|
|
// Calculates the extra height needed for the multiline title, if needed.
|
|
if (collapsingTitleHelper.getExpandedMaxLines() > 1) {
|
|
int lineCount = collapsingTitleHelper.getExpandedLineCount();
|
|
if (lineCount > 1) {
|
|
// Add extra height based on the amount of height beyond the first line of title text.
|
|
int expandedTextHeight =
|
|
Math.round(collapsingTitleHelper.getExpandedTextFullSingleLineHeight());
|
|
extraMultilineTitleHeight = expandedTextHeight * (lineCount - 1);
|
|
} else {
|
|
extraMultilineTitleHeight = 0;
|
|
}
|
|
}
|
|
// Calculates the extra height needed for the multiline subtitle, if needed.
|
|
if (collapsingSubtitleHelper.getExpandedMaxLines() > 1) {
|
|
int lineCount = collapsingSubtitleHelper.getExpandedLineCount();
|
|
if (lineCount > 1) {
|
|
// Add extra height based on the amount of height beyond the first line of subtitle
|
|
// text.
|
|
int expandedTextHeight =
|
|
Math.round(collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight());
|
|
extraMultilineSubtitleHeight = expandedTextHeight * (lineCount - 1);
|
|
} else {
|
|
extraMultilineSubtitleHeight = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (extraHeightForTitles + extraMultilineTitleHeight + extraMultilineSubtitleHeight > 0) {
|
|
heightMeasureSpec =
|
|
MeasureSpec.makeMeasureSpec(
|
|
originalHeight
|
|
+ extraHeightForTitles
|
|
+ extraMultilineTitleHeight
|
|
+ extraMultilineSubtitleHeight,
|
|
MeasureSpec.EXACTLY);
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
}
|
|
|
|
// Set our minimum height to enable proper AppBarLayout collapsing
|
|
if (toolbar != null) {
|
|
if (toolbarDirectChild == null || toolbarDirectChild == this) {
|
|
setMinimumHeight(getHeightWithMargins(toolbar));
|
|
} else {
|
|
setMinimumHeight(getHeightWithMargins(toolbarDirectChild));
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
super.onLayout(changed, left, top, right, bottom);
|
|
|
|
if (lastInsets != null) {
|
|
// Shift down any views which are not set to fit system windows
|
|
final int insetTop = lastInsets.getSystemWindowInsetTop();
|
|
for (int i = 0, z = getChildCount(); i < z; i++) {
|
|
final View child = getChildAt(i);
|
|
if (!child.getFitsSystemWindows()) {
|
|
if (child.getTop() < insetTop) {
|
|
// If the child isn't set to fit system windows but is drawing within
|
|
// the inset offset it down
|
|
ViewCompat.offsetTopAndBottom(child, insetTop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update our child view offset helpers so that they track the correct layout coordinates
|
|
for (int i = 0, z = getChildCount(); i < z; i++) {
|
|
getViewOffsetHelper(getChildAt(i)).onViewLayout();
|
|
}
|
|
|
|
updateTextBounds(left, top, right, bottom, /* forceRecalculate= */ false);
|
|
|
|
updateTitleFromToolbarIfNeeded();
|
|
|
|
updateScrimVisibility();
|
|
|
|
// Apply any view offsets, this should be done at the very end of layout
|
|
for (int i = 0, z = getChildCount(); i < z; i++) {
|
|
getViewOffsetHelper(getChildAt(i)).applyOffsets();
|
|
}
|
|
}
|
|
|
|
private void updateTextBounds(
|
|
int left, int top, int right, int bottom, boolean forceRecalculate) {
|
|
// Update the collapsed bounds by getting its transformed bounds
|
|
if (collapsingTitleEnabled && dummyView != null) {
|
|
// We only draw the title if the dummy view is being displayed (Toolbar removes
|
|
// views if there is no space)
|
|
drawCollapsingTitle = dummyView.isAttachedToWindow() && dummyView.getVisibility() == VISIBLE;
|
|
|
|
if (drawCollapsingTitle || forceRecalculate) {
|
|
final boolean isRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
|
|
|
// Update the collapsed bounds
|
|
updateCollapsedBounds(isRtl);
|
|
|
|
// Update the expanded bounds
|
|
final int titleBoundsLeft = isRtl ? expandedMarginEnd : expandedMarginStart;
|
|
final int titleBoundsTop = tmpRect.top + expandedMarginTop;
|
|
final int titleBoundsRight =
|
|
right - left - (isRtl ? expandedMarginStart : expandedMarginEnd);
|
|
final int titleBoundsBottom = bottom - top - expandedMarginBottom;
|
|
if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
|
|
collapsingTitleHelper.setExpandedBounds(
|
|
titleBoundsLeft, titleBoundsTop, titleBoundsRight, titleBoundsBottom);
|
|
|
|
// Now recalculate using the new bounds
|
|
collapsingTitleHelper.recalculate(forceRecalculate);
|
|
} else {
|
|
collapsingTitleHelper.setExpandedBounds(
|
|
titleBoundsLeft,
|
|
titleBoundsTop,
|
|
titleBoundsRight,
|
|
(int)
|
|
(titleBoundsBottom
|
|
- (collapsingSubtitleHelper.getExpandedTextFullSingleLineHeight()
|
|
+ extraMultilineSubtitleHeight)
|
|
- expandedTitleSpacing),
|
|
/* alignBaselineAtBottom= */ false);
|
|
collapsingSubtitleHelper.setExpandedBounds(
|
|
titleBoundsLeft,
|
|
(int)
|
|
(titleBoundsTop
|
|
+ (collapsingTitleHelper.getExpandedTextFullSingleLineHeight()
|
|
+ extraMultilineTitleHeight)
|
|
+ expandedTitleSpacing),
|
|
titleBoundsRight,
|
|
titleBoundsBottom,
|
|
/* alignBaselineAtBottom= */ false);
|
|
|
|
// Now recalculate using the new bounds
|
|
collapsingTitleHelper.recalculate(forceRecalculate);
|
|
collapsingSubtitleHelper.recalculate(forceRecalculate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateTitleFromToolbarIfNeeded() {
|
|
if (toolbar != null) {
|
|
if (collapsingTitleEnabled) {
|
|
CharSequence title = getToolbarTitle(toolbar);
|
|
if (TextUtils.isEmpty(collapsingTitleHelper.getText()) && !TextUtils.isEmpty(title)) {
|
|
// If we do not currently have a title, try and grab it from the Toolbar
|
|
setTitle(title);
|
|
}
|
|
CharSequence subtitle = getToolbarSubtitle(toolbar);
|
|
if (TextUtils.isEmpty(collapsingSubtitleHelper.getText()) && !TextUtils.isEmpty(subtitle)) {
|
|
// If we do not currently have a subtitle, try and grab it from the Toolbar
|
|
setSubtitle(subtitle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void updateCollapsedBounds(boolean isRtl) {
|
|
final int maxOffset =
|
|
getMaxOffsetForPinChild(toolbarDirectChild != null ? toolbarDirectChild : toolbar);
|
|
DescendantOffsetUtils.getDescendantRect(this, dummyView, tmpRect);
|
|
final int titleMarginStart;
|
|
final int titleMarginEnd;
|
|
final int titleMarginTop;
|
|
final int titleMarginBottom;
|
|
if (toolbar instanceof androidx.appcompat.widget.Toolbar) {
|
|
androidx.appcompat.widget.Toolbar compatToolbar = (androidx.appcompat.widget.Toolbar) toolbar;
|
|
titleMarginStart = compatToolbar.getTitleMarginStart();
|
|
titleMarginEnd = compatToolbar.getTitleMarginEnd();
|
|
titleMarginTop = compatToolbar.getTitleMarginTop();
|
|
titleMarginBottom = compatToolbar.getTitleMarginBottom();
|
|
} else if (VERSION.SDK_INT >= VERSION_CODES.N && toolbar instanceof android.widget.Toolbar) {
|
|
android.widget.Toolbar frameworkToolbar = (android.widget.Toolbar) toolbar;
|
|
titleMarginStart = frameworkToolbar.getTitleMarginStart();
|
|
titleMarginEnd = frameworkToolbar.getTitleMarginEnd();
|
|
titleMarginTop = frameworkToolbar.getTitleMarginTop();
|
|
titleMarginBottom = frameworkToolbar.getTitleMarginBottom();
|
|
} else {
|
|
titleMarginStart = 0;
|
|
titleMarginEnd = 0;
|
|
titleMarginTop = 0;
|
|
titleMarginBottom = 0;
|
|
}
|
|
final int titleBoundsLeft = tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart);
|
|
final int titleBoundsRight = tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd);
|
|
final int titleBoundsTop = tmpRect.top + maxOffset + titleMarginTop;
|
|
final int titleBoundsBottom = tmpRect.bottom + maxOffset - titleMarginBottom;
|
|
final int titleBoundsBottomWithSubtitle =
|
|
(int) (titleBoundsBottom - collapsingSubtitleHelper.getCollapsedFullSingleLineHeight());
|
|
final int subtitleBoundsTop = (int) (titleBoundsTop + collapsingTitleHelper.getCollapsedFullSingleLineHeight());
|
|
|
|
// Setting the valid collapsed bounds that text can be displayed in
|
|
if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
|
|
collapsingTitleHelper.setCollapsedBounds(
|
|
titleBoundsLeft, titleBoundsTop, titleBoundsRight, titleBoundsBottom);
|
|
} else {
|
|
collapsingTitleHelper.setCollapsedBounds(
|
|
titleBoundsLeft,
|
|
titleBoundsTop,
|
|
titleBoundsRight,
|
|
titleBoundsBottomWithSubtitle);
|
|
collapsingSubtitleHelper.setCollapsedBounds(
|
|
titleBoundsLeft,
|
|
subtitleBoundsTop,
|
|
titleBoundsRight,
|
|
titleBoundsBottom);
|
|
}
|
|
|
|
// If the collapsed title gravity should be using the whole collapsing toolbar layout instead of
|
|
// the dummy layout, we should set the collapsed bounds for offsets.
|
|
if (collapsedTitleGravityMode == COLLAPSED_TITLE_GRAVITY_ENTIRE_SPACE) {
|
|
DescendantOffsetUtils.getDescendantRect(this, this, tmpRect);
|
|
final int validTitleBoundsLeft = tmpRect.left + (isRtl ? titleMarginEnd : titleMarginStart);
|
|
final int validTitleBoundsRight = tmpRect.right - (isRtl ? titleMarginStart : titleMarginEnd);
|
|
if (TextUtils.isEmpty(collapsingSubtitleHelper.getText())) {
|
|
collapsingTitleHelper.setCollapsedBoundsForOffsets(
|
|
validTitleBoundsLeft, titleBoundsTop, validTitleBoundsRight, titleBoundsBottom);
|
|
} else {
|
|
collapsingTitleHelper.setCollapsedBoundsForOffsets(
|
|
validTitleBoundsLeft,
|
|
titleBoundsTop,
|
|
validTitleBoundsRight,
|
|
titleBoundsBottomWithSubtitle);
|
|
collapsingSubtitleHelper.setCollapsedBoundsForOffsets(
|
|
validTitleBoundsLeft,
|
|
subtitleBoundsTop,
|
|
validTitleBoundsRight,
|
|
titleBoundsBottom);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static CharSequence getToolbarTitle(View view) {
|
|
if (view instanceof androidx.appcompat.widget.Toolbar) {
|
|
return ((androidx.appcompat.widget.Toolbar) view).getTitle();
|
|
} else if (view instanceof android.widget.Toolbar) {
|
|
return ((android.widget.Toolbar) view).getTitle();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static CharSequence getToolbarSubtitle(View view) {
|
|
if (view instanceof androidx.appcompat.widget.Toolbar) {
|
|
return ((androidx.appcompat.widget.Toolbar) view).getSubtitle();
|
|
} else if (view instanceof android.widget.Toolbar) {
|
|
return ((android.widget.Toolbar) view).getSubtitle();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static int getHeightWithMargins(@NonNull final View view) {
|
|
final ViewGroup.LayoutParams lp = view.getLayoutParams();
|
|
if (lp instanceof MarginLayoutParams) {
|
|
final MarginLayoutParams mlp = (MarginLayoutParams) lp;
|
|
return view.getMeasuredHeight() + mlp.topMargin + mlp.bottomMargin;
|
|
}
|
|
return view.getMeasuredHeight();
|
|
}
|
|
|
|
@NonNull
|
|
static ViewOffsetHelper getViewOffsetHelper(@NonNull View view) {
|
|
ViewOffsetHelper offsetHelper = (ViewOffsetHelper) view.getTag(R.id.view_offset_helper);
|
|
if (offsetHelper == null) {
|
|
offsetHelper = new ViewOffsetHelper(view);
|
|
view.setTag(R.id.view_offset_helper, offsetHelper);
|
|
}
|
|
return offsetHelper;
|
|
}
|
|
|
|
/**
|
|
* Sets the title to be displayed by this view, if enabled.
|
|
*
|
|
* @see #setTitleEnabled(boolean)
|
|
* @see #getTitle()
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_title
|
|
*/
|
|
public void setTitle(@Nullable CharSequence title) {
|
|
collapsingTitleHelper.setText(title);
|
|
updateContentDescriptionFromTitle();
|
|
}
|
|
|
|
/**
|
|
* Returns the title currently being displayed by this view. If the title is not enabled, then
|
|
* this will return {@code null}.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_title
|
|
*/
|
|
@Nullable
|
|
public CharSequence getTitle() {
|
|
return collapsingTitleEnabled ? collapsingTitleHelper.getText() : null;
|
|
}
|
|
|
|
/**
|
|
* Sets the subtitle to be displayed by this view, if enabled.
|
|
*
|
|
* @see #setTitleEnabled(boolean)
|
|
* @see #getSubtitle()
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_subtitle
|
|
*/
|
|
public void setSubtitle(@Nullable CharSequence subtitle) {
|
|
collapsingSubtitleHelper.setText(subtitle);
|
|
}
|
|
|
|
/**
|
|
* Returns the subtitle currently being displayed by this view. If the subtitle is not enabled,
|
|
* then this will return {@code null}.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_subtitle
|
|
*/
|
|
@Nullable
|
|
public CharSequence getSubtitle() {
|
|
return collapsingTitleEnabled ? collapsingSubtitleHelper.getText() : null;
|
|
}
|
|
|
|
/**
|
|
* Sets the title collapse mode which determines the effect used to collapse and expand the title
|
|
* text.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_titleCollapseMode
|
|
*/
|
|
public void setTitleCollapseMode(@TitleCollapseMode int titleCollapseMode) {
|
|
this.titleCollapseMode = titleCollapseMode;
|
|
|
|
boolean fadeModeEnabled = isTitleCollapseFadeMode();
|
|
collapsingTitleHelper.setFadeModeEnabled(fadeModeEnabled);
|
|
collapsingSubtitleHelper.setFadeModeEnabled(fadeModeEnabled);
|
|
|
|
ViewParent parent = getParent();
|
|
if (parent instanceof AppBarLayout) {
|
|
disableLiftOnScrollIfNeeded((AppBarLayout) parent);
|
|
}
|
|
|
|
// If using fade title collapse mode and no content scrim, provide default content scrim.
|
|
if (fadeModeEnabled && contentScrim == null) {
|
|
setContentScrimColor(getDefaultContentScrimColorForTitleCollapseFadeMode());
|
|
}
|
|
}
|
|
|
|
@ColorInt
|
|
private int getDefaultContentScrimColorForTitleCollapseFadeMode() {
|
|
ColorStateList colorSurfaceContainer =
|
|
MaterialColors.getColorStateListOrNull(getContext(), R.attr.colorSurfaceContainer);
|
|
if (colorSurfaceContainer != null) {
|
|
return colorSurfaceContainer.getDefaultColor();
|
|
} else {
|
|
float appBarElevation = getResources().getDimension(R.dimen.design_appbar_elevation);
|
|
return elevationOverlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(
|
|
appBarElevation);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the current title collapse mode.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_titleCollapseMode
|
|
*/
|
|
@TitleCollapseMode
|
|
public int getTitleCollapseMode() {
|
|
return titleCollapseMode;
|
|
}
|
|
|
|
/**
|
|
* Sets whether this view should display its own title.
|
|
*
|
|
* <p>The title displayed by this view will shrink and grow based on the scroll offset.
|
|
*
|
|
* @see #setTitle(CharSequence)
|
|
* @see #isTitleEnabled()
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled
|
|
*/
|
|
public void setTitleEnabled(boolean enabled) {
|
|
if (enabled != collapsingTitleEnabled) {
|
|
collapsingTitleEnabled = enabled;
|
|
updateContentDescriptionFromTitle();
|
|
updateDummyView();
|
|
requestLayout();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether this view is currently displaying its own title.
|
|
*
|
|
* @see #setTitleEnabled(boolean)
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_titleEnabled
|
|
*/
|
|
public boolean isTitleEnabled() {
|
|
return collapsingTitleEnabled;
|
|
}
|
|
|
|
/**
|
|
* Set ellipsizing on the title text.
|
|
*
|
|
* @param ellipsize type of ellipsis behavior
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_titleTextEllipsize
|
|
*/
|
|
public void setTitleEllipsize(@NonNull TruncateAt ellipsize) {
|
|
collapsingTitleHelper.setTitleTextEllipsize(ellipsize);
|
|
}
|
|
|
|
/** Get ellipsizing currently applied on the title text. */
|
|
@NonNull
|
|
public TruncateAt getTitleTextEllipsize() {
|
|
return collapsingTitleHelper.getTitleTextEllipsize();
|
|
}
|
|
|
|
// Convert to supported TruncateAt values
|
|
private TruncateAt convertEllipsizeToTruncateAt(int ellipsize) {
|
|
switch (ellipsize) {
|
|
case 0:
|
|
return TruncateAt.START;
|
|
case 1:
|
|
return TruncateAt.MIDDLE;
|
|
case 3:
|
|
return TruncateAt.MARQUEE;
|
|
case 2:
|
|
default:
|
|
return TruncateAt.END;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set whether the content scrim and/or status bar scrim should be shown or not. Any change in the
|
|
* vertical scroll may overwrite this value. Any visibility change will be animated if this view
|
|
* has already been laid out.
|
|
*
|
|
* @param shown whether the scrims should be shown
|
|
* @see #getStatusBarScrim()
|
|
* @see #getContentScrim()
|
|
*/
|
|
public void setScrimsShown(boolean shown) {
|
|
setScrimsShown(shown, isLaidOut() && !isInEditMode());
|
|
}
|
|
|
|
/**
|
|
* Set whether the content scrim and/or status bar scrim should be shown or not. Any change in the
|
|
* vertical scroll may overwrite this value.
|
|
*
|
|
* @param shown whether the scrims should be shown
|
|
* @param animate whether to animate the visibility change
|
|
* @see #getStatusBarScrim()
|
|
* @see #getContentScrim()
|
|
*/
|
|
public void setScrimsShown(boolean shown, boolean animate) {
|
|
if (scrimsAreShown != shown) {
|
|
if (animate) {
|
|
animateScrim(shown ? 0xFF : 0x0);
|
|
} else {
|
|
setScrimAlpha(shown ? 0xFF : 0x0);
|
|
}
|
|
scrimsAreShown = shown;
|
|
}
|
|
}
|
|
|
|
private void animateScrim(int targetAlpha) {
|
|
ensureToolbar();
|
|
if (scrimAnimator == null) {
|
|
scrimAnimator = new ValueAnimator();
|
|
scrimAnimator.setInterpolator(
|
|
targetAlpha > scrimAlpha
|
|
? scrimAnimationFadeInInterpolator
|
|
: scrimAnimationFadeOutInterpolator);
|
|
scrimAnimator.addUpdateListener(
|
|
new ValueAnimator.AnimatorUpdateListener() {
|
|
@Override
|
|
public void onAnimationUpdate(@NonNull ValueAnimator animator) {
|
|
setScrimAlpha((int) animator.getAnimatedValue());
|
|
}
|
|
});
|
|
} else if (scrimAnimator.isRunning()) {
|
|
scrimAnimator.cancel();
|
|
}
|
|
|
|
scrimAnimator.setDuration(scrimAnimationDuration);
|
|
scrimAnimator.setIntValues(scrimAlpha, targetAlpha);
|
|
scrimAnimator.start();
|
|
}
|
|
|
|
void setScrimAlpha(int alpha) {
|
|
if (alpha != scrimAlpha) {
|
|
final Drawable contentScrim = this.contentScrim;
|
|
if (contentScrim != null && toolbar != null) {
|
|
toolbar.postInvalidateOnAnimation();
|
|
}
|
|
scrimAlpha = alpha;
|
|
postInvalidateOnAnimation();
|
|
}
|
|
}
|
|
|
|
int getScrimAlpha() {
|
|
return scrimAlpha;
|
|
}
|
|
|
|
/**
|
|
* Set the drawable to use for the content scrim from resources. Providing null will disable the
|
|
* scrim functionality.
|
|
*
|
|
* @param drawable the drawable to display
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
|
|
* @see #getContentScrim()
|
|
*/
|
|
public void setContentScrim(@Nullable Drawable drawable) {
|
|
if (contentScrim != drawable) {
|
|
if (contentScrim != null) {
|
|
contentScrim.setCallback(null);
|
|
}
|
|
contentScrim = drawable != null ? drawable.mutate() : null;
|
|
if (contentScrim != null) {
|
|
updateContentScrimBounds(contentScrim, getWidth(), getHeight());
|
|
contentScrim.setCallback(this);
|
|
contentScrim.setAlpha(scrimAlpha);
|
|
}
|
|
postInvalidateOnAnimation();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the color to use for the content scrim.
|
|
*
|
|
* @param color the color to display
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
|
|
* @see #getContentScrim()
|
|
*/
|
|
public void setContentScrimColor(@ColorInt int color) {
|
|
setContentScrim(new ColorDrawable(color));
|
|
}
|
|
|
|
/**
|
|
* Set the drawable to use for the content scrim from resources.
|
|
*
|
|
* @param resId drawable resource id
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
|
|
* @see #getContentScrim()
|
|
*/
|
|
public void setContentScrimResource(@DrawableRes int resId) {
|
|
setContentScrim(getContext().getDrawable(resId));
|
|
}
|
|
|
|
/**
|
|
* Returns the drawable which is used for the foreground scrim.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_contentScrim
|
|
* @see #setContentScrim(Drawable)
|
|
*/
|
|
@Nullable
|
|
public Drawable getContentScrim() {
|
|
return contentScrim;
|
|
}
|
|
|
|
/**
|
|
* Set the drawable to use for the status bar scrim from resources. Providing null will disable
|
|
* the scrim functionality.
|
|
*
|
|
* <p>This scrim is only shown when we have been given a top system inset.
|
|
*
|
|
* @param drawable the drawable to display
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
|
|
* @see #getStatusBarScrim()
|
|
*/
|
|
public void setStatusBarScrim(@Nullable Drawable drawable) {
|
|
if (statusBarScrim != drawable) {
|
|
if (statusBarScrim != null) {
|
|
statusBarScrim.setCallback(null);
|
|
}
|
|
statusBarScrim = drawable != null ? drawable.mutate() : null;
|
|
if (statusBarScrim != null) {
|
|
if (statusBarScrim.isStateful()) {
|
|
statusBarScrim.setState(getDrawableState());
|
|
}
|
|
DrawableCompat.setLayoutDirection(statusBarScrim, getLayoutDirection());
|
|
statusBarScrim.setVisible(getVisibility() == VISIBLE, false);
|
|
statusBarScrim.setCallback(this);
|
|
statusBarScrim.setAlpha(scrimAlpha);
|
|
}
|
|
postInvalidateOnAnimation();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void drawableStateChanged() {
|
|
super.drawableStateChanged();
|
|
|
|
final int[] state = getDrawableState();
|
|
boolean changed = false;
|
|
|
|
Drawable d = statusBarScrim;
|
|
if (d != null && d.isStateful()) {
|
|
changed |= d.setState(state);
|
|
}
|
|
d = contentScrim;
|
|
if (d != null && d.isStateful()) {
|
|
changed |= d.setState(state);
|
|
}
|
|
if (collapsingTitleHelper != null) {
|
|
changed |= collapsingTitleHelper.setState(state);
|
|
}
|
|
|
|
if (changed) {
|
|
invalidate();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected boolean verifyDrawable(@NonNull Drawable who) {
|
|
return super.verifyDrawable(who) || who == contentScrim || who == statusBarScrim;
|
|
}
|
|
|
|
@Override
|
|
public void setVisibility(int visibility) {
|
|
super.setVisibility(visibility);
|
|
|
|
final boolean visible = visibility == VISIBLE;
|
|
if (statusBarScrim != null && statusBarScrim.isVisible() != visible) {
|
|
statusBarScrim.setVisible(visible, false);
|
|
}
|
|
if (contentScrim != null && contentScrim.isVisible() != visible) {
|
|
contentScrim.setVisible(visible, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the color to use for the status bar scrim.
|
|
*
|
|
* <p>This scrim is only shown when we have been given a top system inset.
|
|
*
|
|
* @param color the color to display
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
|
|
* @see #getStatusBarScrim()
|
|
*/
|
|
public void setStatusBarScrimColor(@ColorInt int color) {
|
|
setStatusBarScrim(new ColorDrawable(color));
|
|
}
|
|
|
|
/**
|
|
* Set the drawable to use for the status bar scrim from resources.
|
|
*
|
|
* @param resId drawable resource id
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
|
|
* @see #getStatusBarScrim()
|
|
*/
|
|
public void setStatusBarScrimResource(@DrawableRes int resId) {
|
|
setStatusBarScrim(getContext().getDrawable(resId));
|
|
}
|
|
|
|
/**
|
|
* Returns the drawable which is used for the status bar scrim.
|
|
*
|
|
* @attr ref R.styleable#CollapsingToolbarLayout_statusBarScrim
|
|
* @see #setStatusBarScrim(Drawable)
|
|
*/
|
|
@Nullable
|
|
public Drawable getStatusBarScrim() {
|
|
return statusBarScrim;
|
|
}
|
|
|
|
/**
|
|
* Sets the text color and size for the collapsed title from the specified TextAppearance
|
|
* resource.
|
|
*
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleTextAppearance
|
|
*/
|
|
public void setCollapsedTitleTextAppearance(@StyleRes int resId) {
|
|
collapsingTitleHelper.setCollapsedTextAppearance(resId);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color and size for the collapsed subtitle from the specified TextAppearance
|
|
* resource.
|
|
*
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedSubtitleTextAppearance
|
|
*/
|
|
public void setCollapsedSubtitleTextAppearance(@StyleRes int resId) {
|
|
collapsingSubtitleHelper.setCollapsedTextAppearance(resId);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the collapsed title.
|
|
*
|
|
* @param color The new text color in ARGB format
|
|
*/
|
|
public void setCollapsedTitleTextColor(@ColorInt int color) {
|
|
setCollapsedTitleTextColor(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
/**
|
|
* Sets the text colors of the collapsed title.
|
|
*
|
|
* @param colors ColorStateList containing the new text colors
|
|
*/
|
|
public void setCollapsedTitleTextColor(@NonNull ColorStateList colors) {
|
|
collapsingTitleHelper.setCollapsedTextColor(colors);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the collapsed subtitle.
|
|
*
|
|
* @param color The new text color in ARGB format
|
|
*/
|
|
public void setCollapsedSubtitleTextColor(@ColorInt int color) {
|
|
setCollapsedSubtitleTextColor(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
/**
|
|
* Sets the text colors of the collapsed subtitle.
|
|
*
|
|
* @param colors ColorStateList containing the new text colors
|
|
*/
|
|
public void setCollapsedSubtitleTextColor(@NonNull ColorStateList colors) {
|
|
collapsingSubtitleHelper.setCollapsedTextColor(colors);
|
|
}
|
|
|
|
/**
|
|
* Sets the horizontal alignment of the collapsed titles and the vertical gravity that will be
|
|
* used when there is extra space in the collapsed bounds beyond what is required for the title
|
|
* itself.
|
|
*
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
|
|
*/
|
|
public void setCollapsedTitleGravity(int gravity) {
|
|
collapsingTitleHelper.setCollapsedTextGravity(gravity);
|
|
collapsingSubtitleHelper.setCollapsedTextGravity(gravity);
|
|
}
|
|
|
|
/**
|
|
* Returns the horizontal and vertical alignment for titles when collapsed.
|
|
*
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
|
|
*/
|
|
public int getCollapsedTitleGravity() {
|
|
return collapsingTitleHelper.getCollapsedTextGravity();
|
|
}
|
|
|
|
/**
|
|
* Sets the text color and size for the expanded title from the specified TextAppearance resource.
|
|
*
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleTextAppearance
|
|
*/
|
|
public void setExpandedTitleTextAppearance(@StyleRes int resId) {
|
|
collapsingTitleHelper.setExpandedTextAppearance(resId);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color and size for the expanded subtitle from the specified TextAppearance
|
|
* resource.
|
|
*
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedSubtitleTextAppearance
|
|
*/
|
|
public void setExpandedSubtitleTextAppearance(@StyleRes int resId) {
|
|
collapsingSubtitleHelper.setExpandedTextAppearance(resId);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the expanded title.
|
|
*
|
|
* @param color The new text color in ARGB format
|
|
*/
|
|
public void setExpandedTitleColor(@ColorInt int color) {
|
|
setExpandedTitleTextColor(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
/**
|
|
* Sets the text colors of the expanded title.
|
|
*
|
|
* @param colors ColorStateList containing the new text colors
|
|
*/
|
|
public void setExpandedTitleTextColor(@NonNull ColorStateList colors) {
|
|
collapsingTitleHelper.setExpandedTextColor(colors);
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the expanded subtitle.
|
|
*
|
|
* @param color The new text color in ARGB format
|
|
*/
|
|
public void setExpandedSubtitleColor(@ColorInt int color) {
|
|
setExpandedSubtitleTextColor(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
/**
|
|
* Sets the text colors of the expanded subtitle.
|
|
*
|
|
* @param colors ColorStateList containing the new text colors
|
|
*/
|
|
public void setExpandedSubtitleTextColor(@NonNull ColorStateList colors) {
|
|
collapsingSubtitleHelper.setExpandedTextColor(colors);
|
|
}
|
|
|
|
/**
|
|
* Sets the horizontal alignment of the expanded titles and the vertical gravity that will be used
|
|
* when there is extra space in the expanded bounds beyond what is required for the title itself.
|
|
*
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
|
|
*/
|
|
public void setExpandedTitleGravity(int gravity) {
|
|
collapsingTitleHelper.setExpandedTextGravity(gravity);
|
|
collapsingSubtitleHelper.setExpandedTextGravity(gravity);
|
|
}
|
|
|
|
/**
|
|
* Returns the horizontal and vertical alignment for titles when expanded.
|
|
*
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
|
|
*/
|
|
public int getExpandedTitleGravity() {
|
|
return collapsingTitleHelper.getExpandedTextGravity();
|
|
}
|
|
|
|
/**
|
|
* Sets the text size of the expanded title.
|
|
*
|
|
* @param textSize The text size of the expanded title.
|
|
*/
|
|
public void setExpandedTitleTextSize(float textSize) {
|
|
collapsingTitleHelper.setExpandedTextSize(textSize);
|
|
}
|
|
|
|
/**
|
|
* Sets the text size of the expanded subtitle.
|
|
*
|
|
* @param textSize The text size of the expanded subtitle.
|
|
*/
|
|
public void setExpandedSubtitleTextSize(float textSize) {
|
|
collapsingSubtitleHelper.setExpandedTextSize(textSize);
|
|
}
|
|
|
|
/** Returns the text size of the expanded title. */
|
|
public float getExpandedTitleTextSize() {
|
|
return collapsingTitleHelper.getExpandedTextSize();
|
|
}
|
|
|
|
/** Returns the text size of the expanded subtitle. */
|
|
public float getExpandedSubtitleTextSize() {
|
|
return collapsingSubtitleHelper.getExpandedTextSize();
|
|
}
|
|
|
|
/**
|
|
* Sets the text size of the collapsed title.
|
|
*
|
|
* @param textSize The text size of the collapsed title.
|
|
*/
|
|
public void setCollapsedTitleTextSize(float textSize) {
|
|
collapsingTitleHelper.setCollapsedTextSize(textSize);
|
|
}
|
|
|
|
/**
|
|
* Sets the text size of the collapsed subtitle.
|
|
*
|
|
* @param textSize The text size of the collapsed subtitle.
|
|
*/
|
|
public void setCollapsedSubtitleTextSize(float textSize) {
|
|
collapsingSubtitleHelper.setCollapsedTextSize(textSize);
|
|
}
|
|
|
|
/** Returns the text size of the collapsed title. */
|
|
public float getCollapsedTitleTextSize() {
|
|
return collapsingTitleHelper.getCollapsedTextSize();
|
|
}
|
|
|
|
/** Returns the text size of the collapsed subtitle. */
|
|
public float getCollapsedSubtitleTextSize() {
|
|
return collapsingSubtitleHelper.getCollapsedTextSize();
|
|
}
|
|
|
|
/**
|
|
* Set the typeface to use for the collapsed title.
|
|
*
|
|
* @param typeface typeface to use, or {@code null} to use the default.
|
|
*/
|
|
public void setCollapsedTitleTypeface(@Nullable Typeface typeface) {
|
|
collapsingTitleHelper.setCollapsedTypeface(typeface);
|
|
}
|
|
|
|
/**
|
|
* Set the typeface to use for the collapsed subtitle.
|
|
*
|
|
* @param typeface typeface to use, or {@code null} to use the default.
|
|
*/
|
|
public void setCollapsedSubtitleTypeface(@Nullable Typeface typeface) {
|
|
collapsingSubtitleHelper.setCollapsedTypeface(typeface);
|
|
}
|
|
|
|
/** Returns the typeface used for the collapsed title. */
|
|
@NonNull
|
|
public Typeface getCollapsedTitleTypeface() {
|
|
return collapsingTitleHelper.getCollapsedTypeface();
|
|
}
|
|
|
|
/** Returns the typeface used for the collapsed subtitle. */
|
|
@NonNull
|
|
public Typeface getCollapsedSubtitleTypeface() {
|
|
return collapsingSubtitleHelper.getCollapsedTypeface();
|
|
}
|
|
|
|
/**
|
|
* Set the typeface to use for the expanded title.
|
|
*
|
|
* @param typeface typeface to use, or {@code null} to use the default.
|
|
*/
|
|
public void setExpandedTitleTypeface(@Nullable Typeface typeface) {
|
|
collapsingTitleHelper.setExpandedTypeface(typeface);
|
|
}
|
|
|
|
/**
|
|
* Set the typeface to use for the expanded subtitle.
|
|
*
|
|
* @param typeface typeface to use, or {@code null} to use the default.
|
|
*/
|
|
public void setExpandedSubtitleTypeface(@Nullable Typeface typeface) {
|
|
collapsingSubtitleHelper.setExpandedTypeface(typeface);
|
|
}
|
|
|
|
/** Returns the typeface used for the expanded title. */
|
|
@NonNull
|
|
public Typeface getExpandedTitleTypeface() {
|
|
return collapsingTitleHelper.getExpandedTypeface();
|
|
}
|
|
|
|
/** Returns the typeface used for the expanded subtitle. */
|
|
@NonNull
|
|
public Typeface getExpandedSubtitleTypeface() {
|
|
return collapsingSubtitleHelper.getExpandedTypeface();
|
|
}
|
|
|
|
/**
|
|
* Sets the expanded title margins.
|
|
*
|
|
* @param start the starting title margin in pixels
|
|
* @param top the top title margin in pixels
|
|
* @param end the ending title margin in pixels
|
|
* @param bottom the bottom title margin in pixels
|
|
* @see #getExpandedTitleMarginStart()
|
|
* @see #getExpandedTitleMarginTop()
|
|
* @see #getExpandedTitleMarginEnd()
|
|
* @see #getExpandedTitleMarginBottom()
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMargin
|
|
*/
|
|
public void setExpandedTitleMargin(int start, int top, int end, int bottom) {
|
|
expandedMarginStart = start;
|
|
expandedMarginTop = top;
|
|
expandedMarginEnd = end;
|
|
expandedMarginBottom = bottom;
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* @return the starting expanded title margin in pixels
|
|
* @see #setExpandedTitleMarginStart(int)
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
|
|
*/
|
|
public int getExpandedTitleMarginStart() {
|
|
return expandedMarginStart;
|
|
}
|
|
|
|
/**
|
|
* Sets the starting expanded title margin in pixels.
|
|
*
|
|
* @param margin the starting title margin in pixels
|
|
* @see #getExpandedTitleMarginStart()
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginStart
|
|
*/
|
|
public void setExpandedTitleMarginStart(int margin) {
|
|
expandedMarginStart = margin;
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* @return the top expanded title margin in pixels
|
|
* @see #setExpandedTitleMarginTop(int)
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
|
|
*/
|
|
public int getExpandedTitleMarginTop() {
|
|
return expandedMarginTop;
|
|
}
|
|
|
|
/**
|
|
* Sets the top expanded title margin in pixels.
|
|
*
|
|
* @param margin the top title margin in pixels
|
|
* @see #getExpandedTitleMarginTop()
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginTop
|
|
*/
|
|
public void setExpandedTitleMarginTop(int margin) {
|
|
expandedMarginTop = margin;
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* @return the ending expanded title margin in pixels
|
|
* @see #setExpandedTitleMarginEnd(int)
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
|
|
*/
|
|
public int getExpandedTitleMarginEnd() {
|
|
return expandedMarginEnd;
|
|
}
|
|
|
|
/**
|
|
* Sets the ending expanded title margin in pixels.
|
|
*
|
|
* @param margin the ending title margin in pixels
|
|
* @see #getExpandedTitleMarginEnd()
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
|
|
*/
|
|
public void setExpandedTitleMarginEnd(int margin) {
|
|
expandedMarginEnd = margin;
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* @return the bottom expanded title margin in pixels
|
|
* @see #setExpandedTitleMarginBottom(int)
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
|
|
*/
|
|
public int getExpandedTitleMarginBottom() {
|
|
return expandedMarginBottom;
|
|
}
|
|
|
|
/**
|
|
* Sets the bottom expanded title margin in pixels.
|
|
*
|
|
* @param margin the bottom title margin in pixels
|
|
* @see #getExpandedTitleMarginBottom()
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
|
|
*/
|
|
public void setExpandedTitleMarginBottom(int margin) {
|
|
expandedMarginBottom = margin;
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* Returns the spacing between the expanded title and subtitle in pixels
|
|
*
|
|
* @see #setExpandedTitleSpacing(int)
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing
|
|
*/
|
|
public int getExpandedTitleSpacing() {
|
|
return expandedTitleSpacing;
|
|
}
|
|
|
|
/**
|
|
* Sets the spacing between the expanded title and subtitle.
|
|
*
|
|
* @param titleSpacing the spacing between the expanded title and subtitle in pixels
|
|
* @see #getExpandedTitleSpacing()
|
|
* @attr ref com.google.android.material.R.styleable#CollapsingToolbarLayout_expandedTitleSpacing
|
|
*/
|
|
public void setExpandedTitleSpacing(int titleSpacing) {
|
|
expandedTitleSpacing = titleSpacing;
|
|
requestLayout();
|
|
}
|
|
|
|
/** Sets the maximum number of lines to display in the expanded state. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public void setMaxLines(int maxLines) {
|
|
collapsingTitleHelper.setExpandedMaxLines(maxLines);
|
|
collapsingSubtitleHelper.setExpandedMaxLines(maxLines);
|
|
}
|
|
|
|
/** Gets the maximum number of lines to display in the expanded state. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public int getMaxLines() {
|
|
return collapsingTitleHelper.getExpandedMaxLines();
|
|
}
|
|
|
|
/** Gets the current number of lines of the title text. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public int getLineCount() {
|
|
return collapsingTitleHelper.getLineCount();
|
|
}
|
|
|
|
/**
|
|
* Sets the line spacing addition of the title text. See {@link
|
|
* android.widget.TextView#setLineSpacing(float, float)}. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public void setLineSpacingAdd(float spacingAdd) {
|
|
collapsingTitleHelper.setLineSpacingAdd(spacingAdd);
|
|
}
|
|
|
|
/** Gets the line spacing addition of the title text, or -1 if not set. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public float getLineSpacingAdd() {
|
|
return collapsingTitleHelper.getLineSpacingAdd();
|
|
}
|
|
|
|
/**
|
|
* Sets the line spacing multiplier of the title text. See {@link
|
|
* android.widget.TextView#setLineSpacing(float, float)}. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public void setLineSpacingMultiplier(@FloatRange(from = 0.0) float spacingMultiplier) {
|
|
collapsingTitleHelper.setLineSpacingMultiplier(spacingMultiplier);
|
|
}
|
|
|
|
/** Gets the line spacing multiplier of the title text, or -1 if not set. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public float getLineSpacingMultiplier() {
|
|
return collapsingTitleHelper.getLineSpacingMultiplier();
|
|
}
|
|
|
|
/**
|
|
* Sets the hyphenation frequency of the title text. See {@link
|
|
* android.widget.TextView#setHyphenationFrequency(int)}. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public void setHyphenationFrequency(int hyphenationFrequency) {
|
|
collapsingTitleHelper.setHyphenationFrequency(hyphenationFrequency);
|
|
}
|
|
|
|
/** Gets the hyphenation frequency of the title text, or -1 if not set. Experimental Feature. */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public int getHyphenationFrequency() {
|
|
return collapsingTitleHelper.getHyphenationFrequency();
|
|
}
|
|
|
|
/**
|
|
* Sets the {@link StaticLayoutBuilderConfigurer} for the {@link android.text.StaticLayout} of the
|
|
* title text.
|
|
*
|
|
* <p>Note that the updates to the {@link android.text.StaticLayout.Builder} in the configure
|
|
* method (e.g., the text, alignment, max lines, line spacing, etc.) will take precedence over and
|
|
* overwrite any previous configurations made by the {@link CollapsingToolbarLayout} to the same
|
|
* properties.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public void setStaticLayoutBuilderConfigurer(
|
|
@Nullable StaticLayoutBuilderConfigurer staticLayoutBuilderConfigurer) {
|
|
collapsingTitleHelper.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer);
|
|
}
|
|
|
|
/**
|
|
* Sets whether {@code TextDirectionHeuristics} should be used to determine whether the title text
|
|
* is RTL. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public void setRtlTextDirectionHeuristicsEnabled(boolean rtlTextDirectionHeuristicsEnabled) {
|
|
collapsingTitleHelper.setRtlTextDirectionHeuristicsEnabled(rtlTextDirectionHeuristicsEnabled);
|
|
}
|
|
|
|
/**
|
|
* Gets whether {@code TextDirectionHeuristics} should be used to determine whether the title text
|
|
* is RTL. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public boolean isRtlTextDirectionHeuristicsEnabled() {
|
|
return collapsingTitleHelper.isRtlTextDirectionHeuristicsEnabled();
|
|
}
|
|
|
|
/**
|
|
* Sets whether the top system window inset should be respected regardless of what the {@code
|
|
* layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public void setForceApplySystemWindowInsetTop(boolean forceApplySystemWindowInsetTop) {
|
|
this.forceApplySystemWindowInsetTop = forceApplySystemWindowInsetTop;
|
|
}
|
|
|
|
/**
|
|
* Gets whether the top system window inset should be respected regardless of what the {@code
|
|
* layout_height} of the {@code CollapsingToolbarLayout} is set to. Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public boolean isForceApplySystemWindowInsetTop() {
|
|
return forceApplySystemWindowInsetTop;
|
|
}
|
|
|
|
/**
|
|
* Sets whether extra height should be added when the title text spans across multiple lines.
|
|
* Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public void setExtraMultilineHeightEnabled(boolean extraMultilineHeightEnabled) {
|
|
this.extraMultilineHeightEnabled = extraMultilineHeightEnabled;
|
|
}
|
|
|
|
/**
|
|
* Gets whether extra height should be added when the title text spans across multiple lines.
|
|
* Experimental Feature.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public boolean isExtraMultilineHeightEnabled() {
|
|
return extraMultilineHeightEnabled;
|
|
}
|
|
|
|
/**
|
|
* Set the amount of visible height in pixels used to define when to trigger a scrim visibility
|
|
* change.
|
|
*
|
|
* <p>If the visible height of this view is less than the given value, the scrims will be made
|
|
* visible, otherwise they are hidden.
|
|
*
|
|
* @param height value in pixels used to define when to trigger a scrim visibility change
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimVisibleHeightTrigger
|
|
*/
|
|
public void setScrimVisibleHeightTrigger(@IntRange(from = 0) final int height) {
|
|
if (scrimVisibleHeightTrigger != height) {
|
|
scrimVisibleHeightTrigger = height;
|
|
// Update the scrim visibility
|
|
updateScrimVisibility();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the amount of visible height in pixels used to define when to trigger a scrim
|
|
* visibility change.
|
|
*
|
|
* @see #setScrimVisibleHeightTrigger(int)
|
|
*/
|
|
public int getScrimVisibleHeightTrigger() {
|
|
if (scrimVisibleHeightTrigger >= 0) {
|
|
// If we have one explicitly set, return it
|
|
return scrimVisibleHeightTrigger
|
|
+ topInsetApplied
|
|
+ extraMultilineTitleHeight
|
|
+ extraMultilineSubtitleHeight
|
|
+ extraHeightForTitles;
|
|
}
|
|
|
|
// Otherwise we'll use the default computed value
|
|
final int insetTop = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;
|
|
|
|
final int minHeight = getMinimumHeight();
|
|
if (minHeight > 0) {
|
|
// If we have a minHeight set, lets use 2 * minHeight (capped at our height)
|
|
return Math.min((minHeight * 2) + insetTop, getHeight());
|
|
}
|
|
|
|
// If we reach here then we don't have a min height set. Instead we'll take a
|
|
// guess at 1/3 of our height being visible
|
|
return getHeight() / 3;
|
|
}
|
|
|
|
/**
|
|
* Sets the interpolator to use when animating the title position from collapsed to expanded and
|
|
* vice versa.
|
|
*
|
|
* @param interpolator the interpolator to use.
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_titlePositionInterpolator
|
|
*/
|
|
public void setTitlePositionInterpolator(@Nullable TimeInterpolator interpolator) {
|
|
collapsingTitleHelper.setPositionInterpolator(interpolator);
|
|
}
|
|
|
|
/**
|
|
* Returns the interpolator being used to animate the title position from collapsed to expanded
|
|
* and vice versa.
|
|
*/
|
|
@Nullable
|
|
public TimeInterpolator getTitlePositionInterpolator() {
|
|
return collapsingTitleHelper.getPositionInterpolator();
|
|
}
|
|
|
|
/**
|
|
* Set the duration used for scrim visibility animations.
|
|
*
|
|
* @param duration the duration to use in milliseconds
|
|
* @attr ref
|
|
* com.google.android.material.R.styleable#CollapsingToolbarLayout_scrimAnimationDuration
|
|
*/
|
|
public void setScrimAnimationDuration(@IntRange(from = 0) final long duration) {
|
|
scrimAnimationDuration = duration;
|
|
}
|
|
|
|
/** Returns the duration in milliseconds used for scrim visibility animations. */
|
|
public long getScrimAnimationDuration() {
|
|
return scrimAnimationDuration;
|
|
}
|
|
|
|
@Override
|
|
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
|
return p instanceof LayoutParams;
|
|
}
|
|
|
|
@Override
|
|
protected LayoutParams generateDefaultLayoutParams() {
|
|
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
|
}
|
|
|
|
@Override
|
|
public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
|
return new LayoutParams(getContext(), attrs);
|
|
}
|
|
|
|
@Override
|
|
protected FrameLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
|
return new LayoutParams(p);
|
|
}
|
|
|
|
public static class LayoutParams extends FrameLayout.LayoutParams {
|
|
|
|
private static final float DEFAULT_PARALLAX_MULTIPLIER = 0.5f;
|
|
|
|
/** @hide */
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@IntDef({COLLAPSE_MODE_OFF, COLLAPSE_MODE_PIN, COLLAPSE_MODE_PARALLAX})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@interface CollapseMode {}
|
|
|
|
/** The view will act as normal with no collapsing behavior. */
|
|
public static final int COLLAPSE_MODE_OFF = 0;
|
|
|
|
/**
|
|
* The view will pin in place until it reaches the bottom of the {@link
|
|
* CollapsingToolbarLayout}.
|
|
*/
|
|
public static final int COLLAPSE_MODE_PIN = 1;
|
|
|
|
/**
|
|
* The view will scroll in a parallax fashion. See {@link #setParallaxMultiplier(float)} to
|
|
* change the multiplier used.
|
|
*/
|
|
public static final int COLLAPSE_MODE_PARALLAX = 2;
|
|
|
|
int collapseMode = COLLAPSE_MODE_OFF;
|
|
float parallaxMult = DEFAULT_PARALLAX_MULTIPLIER;
|
|
|
|
public LayoutParams(Context c, AttributeSet attrs) {
|
|
super(c, attrs);
|
|
|
|
TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CollapsingToolbarLayout_Layout);
|
|
collapseMode =
|
|
a.getInt(
|
|
R.styleable.CollapsingToolbarLayout_Layout_layout_collapseMode, COLLAPSE_MODE_OFF);
|
|
setParallaxMultiplier(
|
|
a.getFloat(
|
|
R.styleable.CollapsingToolbarLayout_Layout_layout_collapseParallaxMultiplier,
|
|
DEFAULT_PARALLAX_MULTIPLIER));
|
|
a.recycle();
|
|
}
|
|
|
|
public LayoutParams(int width, int height) {
|
|
super(width, height);
|
|
}
|
|
|
|
public LayoutParams(int width, int height, int gravity) {
|
|
super(width, height, gravity);
|
|
}
|
|
|
|
public LayoutParams(@NonNull ViewGroup.LayoutParams p) {
|
|
super(p);
|
|
}
|
|
|
|
public LayoutParams(@NonNull MarginLayoutParams source) {
|
|
super(source);
|
|
}
|
|
|
|
public LayoutParams(@NonNull FrameLayout.LayoutParams source) {
|
|
// The copy constructor called here only exists on API 19+.
|
|
super(source);
|
|
}
|
|
|
|
public LayoutParams(@NonNull LayoutParams source) {
|
|
// The copy constructor called here only exists on API 19+.
|
|
super(source);
|
|
collapseMode = source.collapseMode;
|
|
parallaxMult = source.parallaxMult;
|
|
}
|
|
|
|
/**
|
|
* Set the collapse mode.
|
|
*
|
|
* @param collapseMode one of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN} or {@link
|
|
* #COLLAPSE_MODE_PARALLAX}.
|
|
*/
|
|
public void setCollapseMode(@CollapseMode int collapseMode) {
|
|
this.collapseMode = collapseMode;
|
|
}
|
|
|
|
/**
|
|
* Returns the requested collapse mode.
|
|
*
|
|
* @return the current mode. One of {@link #COLLAPSE_MODE_OFF}, {@link #COLLAPSE_MODE_PIN} or
|
|
* {@link #COLLAPSE_MODE_PARALLAX}.
|
|
*/
|
|
@CollapseMode
|
|
public int getCollapseMode() {
|
|
return collapseMode;
|
|
}
|
|
|
|
/**
|
|
* Set the parallax scroll multiplier used in conjunction with {@link #COLLAPSE_MODE_PARALLAX}.
|
|
* A value of {@code 0.0} indicates no movement at all, {@code 1.0f} indicates normal scroll
|
|
* movement.
|
|
*
|
|
* @param multiplier the multiplier.
|
|
* @see #getParallaxMultiplier()
|
|
*/
|
|
public void setParallaxMultiplier(float multiplier) {
|
|
parallaxMult = multiplier;
|
|
}
|
|
|
|
/**
|
|
* Returns the parallax scroll multiplier used in conjunction with {@link
|
|
* #COLLAPSE_MODE_PARALLAX}.
|
|
*
|
|
* @see #setParallaxMultiplier(float)
|
|
*/
|
|
public float getParallaxMultiplier() {
|
|
return parallaxMult;
|
|
}
|
|
}
|
|
|
|
/** Show or hide the scrims if needed */
|
|
final void updateScrimVisibility() {
|
|
if (contentScrim != null || statusBarScrim != null) {
|
|
setScrimsShown(getHeight() + currentOffset < getScrimVisibleHeightTrigger());
|
|
}
|
|
}
|
|
|
|
final int getMaxOffsetForPinChild(@NonNull View child) {
|
|
final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
return getHeight() - offsetHelper.getLayoutTop() - child.getHeight() - lp.bottomMargin;
|
|
}
|
|
|
|
private void updateContentDescriptionFromTitle() {
|
|
// Set this layout's contentDescription to match the title if it's shown by CollapsingTextHelper
|
|
setContentDescription(getTitle());
|
|
}
|
|
|
|
private class OffsetUpdateListener implements AppBarLayout.OnOffsetChangedListener {
|
|
OffsetUpdateListener() {}
|
|
|
|
@Override
|
|
public void onOffsetChanged(AppBarLayout layout, int verticalOffset) {
|
|
currentOffset = verticalOffset;
|
|
|
|
final int insetTop = lastInsets != null ? lastInsets.getSystemWindowInsetTop() : 0;
|
|
|
|
for (int i = 0, z = getChildCount(); i < z; i++) {
|
|
final View child = getChildAt(i);
|
|
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
final ViewOffsetHelper offsetHelper = getViewOffsetHelper(child);
|
|
|
|
switch (lp.collapseMode) {
|
|
case LayoutParams.COLLAPSE_MODE_PIN:
|
|
offsetHelper.setTopAndBottomOffset(
|
|
MathUtils.clamp(-verticalOffset, 0, getMaxOffsetForPinChild(child)));
|
|
break;
|
|
case LayoutParams.COLLAPSE_MODE_PARALLAX:
|
|
offsetHelper.setTopAndBottomOffset(Math.round(-verticalOffset * lp.parallaxMult));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Show or hide the scrims if needed
|
|
updateScrimVisibility();
|
|
|
|
if (statusBarScrim != null && insetTop > 0) {
|
|
postInvalidateOnAnimation();
|
|
}
|
|
|
|
// Update the collapsing text's fraction
|
|
int height = getHeight();
|
|
final int expandRange = height - getMinimumHeight() - insetTop;
|
|
final int scrimRange = height - getScrimVisibleHeightTrigger();
|
|
final int currentOffsetY = currentOffset + expandRange;
|
|
final float expansionFraction = Math.abs(verticalOffset) / (float) expandRange;
|
|
collapsingTitleHelper.setFadeModeStartFraction(
|
|
Math.min(1, (float) scrimRange / (float) expandRange));
|
|
collapsingTitleHelper.setCurrentOffsetY(currentOffsetY);
|
|
collapsingTitleHelper.setExpansionFraction(expansionFraction);
|
|
collapsingSubtitleHelper.setFadeModeStartFraction(
|
|
Math.min(1, (float) scrimRange / (float) expandRange));
|
|
collapsingSubtitleHelper.setCurrentOffsetY(currentOffsetY);
|
|
collapsingSubtitleHelper.setExpansionFraction(expansionFraction);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interface that allows for further customization of the provided {@link
|
|
* android.text.StaticLayout}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
@RequiresApi(VERSION_CODES.M)
|
|
public interface StaticLayoutBuilderConfigurer
|
|
extends com.google.android.material.internal.StaticLayoutBuilderConfigurer {}
|
|
}
|