FloatingActionButton.java revision be48ed9161c09c4b2178ab6dbe28638222809fc7
19840efe3dbdc7026521da8576574c55120782f6cChris Banes/*
29840efe3dbdc7026521da8576574c55120782f6cChris Banes * Copyright (C) 2015 The Android Open Source Project
39840efe3dbdc7026521da8576574c55120782f6cChris Banes *
49840efe3dbdc7026521da8576574c55120782f6cChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
59840efe3dbdc7026521da8576574c55120782f6cChris Banes * you may not use this file except in compliance with the License.
69840efe3dbdc7026521da8576574c55120782f6cChris Banes * You may obtain a copy of the License at
79840efe3dbdc7026521da8576574c55120782f6cChris Banes *
89840efe3dbdc7026521da8576574c55120782f6cChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
99840efe3dbdc7026521da8576574c55120782f6cChris Banes *
109840efe3dbdc7026521da8576574c55120782f6cChris Banes * Unless required by applicable law or agreed to in writing, software
119840efe3dbdc7026521da8576574c55120782f6cChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
129840efe3dbdc7026521da8576574c55120782f6cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139840efe3dbdc7026521da8576574c55120782f6cChris Banes * See the License for the specific language governing permissions and
149840efe3dbdc7026521da8576574c55120782f6cChris Banes * limitations under the License.
159840efe3dbdc7026521da8576574c55120782f6cChris Banes */
169840efe3dbdc7026521da8576574c55120782f6cChris Banes
179840efe3dbdc7026521da8576574c55120782f6cChris Banespackage android.support.design.widget;
189840efe3dbdc7026521da8576574c55120782f6cChris Banes
199840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.annotation.TargetApi;
209840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.Context;
219840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.ColorStateList;
229840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.TypedArray;
239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff;
249840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.Rect;
259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable;
269840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.os.Build;
279840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.annotation.Nullable;
289840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.design.R;
29b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat;
309840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.util.AttributeSet;
3114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.View;
329840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.widget.ImageView;
339840efe3dbdc7026521da8576574c55120782f6cChris Banes
34a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List;
35b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
369840efe3dbdc7026521da8576574c55120782f6cChris Banes/**
379840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons are used for a special type of promoted action. They are distinguished
3814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * by a circled icon floating above the UI and have special motion behaviors related to morphing,
399840efe3dbdc7026521da8576574c55120782f6cChris Banes * launching, and the transferring anchor point.
409840efe3dbdc7026521da8576574c55120782f6cChris Banes *
419fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>Floating action buttons come in two sizes: the default and the mini. The size can be
429fb154338a62edc2c57dc036895199d6f1769400Chris Banes * controlled with the {@code fabSize} attribute.</p>
439fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
449fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed
459fb154338a62edc2c57dc036895199d6f1769400Chris Banes * via {@link #setImageDrawable(Drawable)}.</p>
469fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
479fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you
489fb154338a62edc2c57dc036895199d6f1769400Chris Banes * wish to change this at runtime then you can do so via
499fb154338a62edc2c57dc036895199d6f1769400Chris Banes * {@link #setBackgroundTintList(ColorStateList)}.</p>
509fb154338a62edc2c57dc036895199d6f1769400Chris Banes *
519fb154338a62edc2c57dc036895199d6f1769400Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize
529840efe3dbdc7026521da8576574c55120782f6cChris Banes */
53b7f9224b1495db47eb8fd813b5912250e900770aChris Banes@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
549840efe3dbdc7026521da8576574c55120782f6cChris Banespublic class FloatingActionButton extends ImageView {
559840efe3dbdc7026521da8576574c55120782f6cChris Banes
569840efe3dbdc7026521da8576574c55120782f6cChris Banes    // These values must match those in the attrs declaration
579840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static final int SIZE_MINI = 1;
589840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static final int SIZE_NORMAL = 0;
599840efe3dbdc7026521da8576574c55120782f6cChris Banes
609840efe3dbdc7026521da8576574c55120782f6cChris Banes    private ColorStateList mBackgroundTint;
619840efe3dbdc7026521da8576574c55120782f6cChris Banes    private PorterDuff.Mode mBackgroundTintMode;
629840efe3dbdc7026521da8576574c55120782f6cChris Banes
63cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes    private int mBorderWidth;
649840efe3dbdc7026521da8576574c55120782f6cChris Banes    private int mRippleColor;
659840efe3dbdc7026521da8576574c55120782f6cChris Banes    private int mSize;
669840efe3dbdc7026521da8576574c55120782f6cChris Banes    private int mContentPadding;
679840efe3dbdc7026521da8576574c55120782f6cChris Banes
689840efe3dbdc7026521da8576574c55120782f6cChris Banes    private final Rect mShadowPadding;
699840efe3dbdc7026521da8576574c55120782f6cChris Banes
709840efe3dbdc7026521da8576574c55120782f6cChris Banes    private final FloatingActionButtonImpl mImpl;
719840efe3dbdc7026521da8576574c55120782f6cChris Banes
729840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context) {
739840efe3dbdc7026521da8576574c55120782f6cChris Banes        this(context, null);
749840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
759840efe3dbdc7026521da8576574c55120782f6cChris Banes
769840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context, AttributeSet attrs) {
779840efe3dbdc7026521da8576574c55120782f6cChris Banes        this(context, attrs, 0);
789840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
799840efe3dbdc7026521da8576574c55120782f6cChris Banes
809840efe3dbdc7026521da8576574c55120782f6cChris Banes    public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
819840efe3dbdc7026521da8576574c55120782f6cChris Banes        super(context, attrs, defStyleAttr);
829840efe3dbdc7026521da8576574c55120782f6cChris Banes
839840efe3dbdc7026521da8576574c55120782f6cChris Banes        mShadowPadding = new Rect();
849840efe3dbdc7026521da8576574c55120782f6cChris Banes
859840efe3dbdc7026521da8576574c55120782f6cChris Banes        TypedArray a = context.obtainStyledAttributes(attrs,
869840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton, defStyleAttr,
879840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.style.Widget_Design_FloatingActionButton);
889840efe3dbdc7026521da8576574c55120782f6cChris Banes        Drawable background = a.getDrawable(R.styleable.FloatingActionButton_android_background);
899840efe3dbdc7026521da8576574c55120782f6cChris Banes        mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
909840efe3dbdc7026521da8576574c55120782f6cChris Banes        mBackgroundTintMode = parseTintMode(a.getInt(
919840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
929840efe3dbdc7026521da8576574c55120782f6cChris Banes        mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
939840efe3dbdc7026521da8576574c55120782f6cChris Banes        mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL);
94cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes        mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0);
959840efe3dbdc7026521da8576574c55120782f6cChris Banes        final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f);
969840efe3dbdc7026521da8576574c55120782f6cChris Banes        final float pressedTranslationZ = a.getDimension(
979840efe3dbdc7026521da8576574c55120782f6cChris Banes                R.styleable.FloatingActionButton_pressedTranslationZ, 0f);
989840efe3dbdc7026521da8576574c55120782f6cChris Banes        a.recycle();
999840efe3dbdc7026521da8576574c55120782f6cChris Banes
1009840efe3dbdc7026521da8576574c55120782f6cChris Banes        final ShadowViewDelegate delegate = new ShadowViewDelegate() {
1019840efe3dbdc7026521da8576574c55120782f6cChris Banes            @Override
1029840efe3dbdc7026521da8576574c55120782f6cChris Banes            public float getRadius() {
1039840efe3dbdc7026521da8576574c55120782f6cChris Banes                return getSizeDimension() / 2f;
1049840efe3dbdc7026521da8576574c55120782f6cChris Banes            }
1059840efe3dbdc7026521da8576574c55120782f6cChris Banes
1069840efe3dbdc7026521da8576574c55120782f6cChris Banes            @Override
1079840efe3dbdc7026521da8576574c55120782f6cChris Banes            public void setShadowPadding(int left, int top, int right, int bottom) {
1089840efe3dbdc7026521da8576574c55120782f6cChris Banes                mShadowPadding.set(left, top, right, bottom);
1099840efe3dbdc7026521da8576574c55120782f6cChris Banes
1109840efe3dbdc7026521da8576574c55120782f6cChris Banes                setPadding(left + mContentPadding, top + mContentPadding,
1119840efe3dbdc7026521da8576574c55120782f6cChris Banes                        right + mContentPadding, bottom + mContentPadding);
1129840efe3dbdc7026521da8576574c55120782f6cChris Banes            }
1139840efe3dbdc7026521da8576574c55120782f6cChris Banes
1149840efe3dbdc7026521da8576574c55120782f6cChris Banes            @Override
1159840efe3dbdc7026521da8576574c55120782f6cChris Banes            public void setBackgroundDrawable(Drawable background) {
1169840efe3dbdc7026521da8576574c55120782f6cChris Banes                FloatingActionButton.super.setBackgroundDrawable(background);
1179840efe3dbdc7026521da8576574c55120782f6cChris Banes            }
1189840efe3dbdc7026521da8576574c55120782f6cChris Banes        };
1199840efe3dbdc7026521da8576574c55120782f6cChris Banes
120be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        final int sdk = Build.VERSION.SDK_INT;
121be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        if (sdk >= 21) {
1229840efe3dbdc7026521da8576574c55120782f6cChris Banes            mImpl = new FloatingActionButtonLollipop(this, delegate);
123be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        } else if (sdk >= 12) {
124be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            mImpl = new FloatingActionButtonHoneycombMr1(this, delegate);
1259840efe3dbdc7026521da8576574c55120782f6cChris Banes        } else {
1269840efe3dbdc7026521da8576574c55120782f6cChris Banes            mImpl = new FloatingActionButtonEclairMr1(this, delegate);
1279840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
1289840efe3dbdc7026521da8576574c55120782f6cChris Banes
1299840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int maxContentSize = (int) getResources().getDimension(R.dimen.fab_content_size);
1309840efe3dbdc7026521da8576574c55120782f6cChris Banes        mContentPadding = (getSizeDimension() - maxContentSize) / 2;
1319840efe3dbdc7026521da8576574c55120782f6cChris Banes
1329840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.setBackgroundDrawable(background, mBackgroundTint,
133cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes                mBackgroundTintMode, mRippleColor, mBorderWidth);
1349840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.setElevation(elevation);
1359840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.setPressedTranslationZ(pressedTranslationZ);
136d039e3555848f678a2e5363e99026df322d02044Taeho Kim
137d039e3555848f678a2e5363e99026df322d02044Taeho Kim        setClickable(true);
1389840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1399840efe3dbdc7026521da8576574c55120782f6cChris Banes
1409840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
1419840efe3dbdc7026521da8576574c55120782f6cChris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1429840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int preferredSize = getSizeDimension();
1439840efe3dbdc7026521da8576574c55120782f6cChris Banes
1449840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec);
1459840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec);
1469840efe3dbdc7026521da8576574c55120782f6cChris Banes
1479840efe3dbdc7026521da8576574c55120782f6cChris Banes        // As we want to stay circular, we set both dimensions to be the
1489840efe3dbdc7026521da8576574c55120782f6cChris Banes        // smallest resolved dimension
1499840efe3dbdc7026521da8576574c55120782f6cChris Banes        final int d = Math.min(w, h);
1509840efe3dbdc7026521da8576574c55120782f6cChris Banes
1519840efe3dbdc7026521da8576574c55120782f6cChris Banes        // We add the shadow's padding to the measured dimension
1529840efe3dbdc7026521da8576574c55120782f6cChris Banes        setMeasuredDimension(
1539840efe3dbdc7026521da8576574c55120782f6cChris Banes                d + mShadowPadding.left + mShadowPadding.right,
1549840efe3dbdc7026521da8576574c55120782f6cChris Banes                d + mShadowPadding.top + mShadowPadding.bottom);
1559840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1569840efe3dbdc7026521da8576574c55120782f6cChris Banes
1579840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1589840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Set the ripple color for this {@link FloatingActionButton}.
1599840efe3dbdc7026521da8576574c55120782f6cChris Banes     * <p>
1609840efe3dbdc7026521da8576574c55120782f6cChris Banes     * When running on devices with KitKat or below, we draw a fill rather than a ripple.
1619840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1629840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param color ARGB color to use for the ripple.
1639840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1649840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setRippleColor(int color) {
1659840efe3dbdc7026521da8576574c55120782f6cChris Banes        if (mRippleColor != color) {
1669840efe3dbdc7026521da8576574c55120782f6cChris Banes            mRippleColor = color;
1679840efe3dbdc7026521da8576574c55120782f6cChris Banes            mImpl.setRippleColor(color);
1689840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
1699840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1709840efe3dbdc7026521da8576574c55120782f6cChris Banes
1719840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1729840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Return the tint applied to the background drawable, if specified.
1739840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1749840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @return the tint applied to the background drawable
1759840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @see #setBackgroundTintList(ColorStateList)
1769840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1779840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Nullable
1789840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
1799840efe3dbdc7026521da8576574c55120782f6cChris Banes    public ColorStateList getBackgroundTintList() {
1809840efe3dbdc7026521da8576574c55120782f6cChris Banes        return mBackgroundTint;
1819840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1829840efe3dbdc7026521da8576574c55120782f6cChris Banes
1839840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1849840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Applies a tint to the background drawable. Does not modify the current tint
1859840efe3dbdc7026521da8576574c55120782f6cChris Banes     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
1869840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1879840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param tint the tint to apply, may be {@code null} to clear tint
1889840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
1899840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setBackgroundTintList(@Nullable ColorStateList tint) {
1909840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.setBackgroundTintList(tint);
1919840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
1929840efe3dbdc7026521da8576574c55120782f6cChris Banes
1939840efe3dbdc7026521da8576574c55120782f6cChris Banes
1949840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
1959840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Return the blending mode used to apply the tint to the background
1969840efe3dbdc7026521da8576574c55120782f6cChris Banes     * drawable, if specified.
1979840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
1989840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @return the blending mode used to apply the tint to the background
1999840efe3dbdc7026521da8576574c55120782f6cChris Banes     *         drawable
2009840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @see #setBackgroundTintMode(PorterDuff.Mode)
2019840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
2029840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Nullable
2039840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
2049840efe3dbdc7026521da8576574c55120782f6cChris Banes    public PorterDuff.Mode getBackgroundTintMode() {
2059840efe3dbdc7026521da8576574c55120782f6cChris Banes        return mBackgroundTintMode;
2069840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2079840efe3dbdc7026521da8576574c55120782f6cChris Banes
2089840efe3dbdc7026521da8576574c55120782f6cChris Banes    /**
2099840efe3dbdc7026521da8576574c55120782f6cChris Banes     * Specifies the blending mode used to apply the tint specified by
2109840efe3dbdc7026521da8576574c55120782f6cChris Banes     * {@link #setBackgroundTintList(ColorStateList)}} to the background
2119840efe3dbdc7026521da8576574c55120782f6cChris Banes     * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
2129840efe3dbdc7026521da8576574c55120782f6cChris Banes     *
2139840efe3dbdc7026521da8576574c55120782f6cChris Banes     * @param tintMode the blending mode used to apply the tint, may be
2149840efe3dbdc7026521da8576574c55120782f6cChris Banes     *                 {@code null} to clear tint
2159840efe3dbdc7026521da8576574c55120782f6cChris Banes     */
2169840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
2179840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.setBackgroundTintMode(tintMode);
2189840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2199840efe3dbdc7026521da8576574c55120782f6cChris Banes
2209840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
2219840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void setBackgroundDrawable(Drawable background) {
222d039e3555848f678a2e5363e99026df322d02044Taeho Kim        if (mImpl != null) {
223d039e3555848f678a2e5363e99026df322d02044Taeho Kim            mImpl.setBackgroundDrawable(
224be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                    background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
225be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
226be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    }
227be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
228be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    /**
229be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * Shows the button.
230be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * <p>This method will animate it the button show if the view has already been laid out.</p>
231be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     */
232be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    public void show() {
233be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        if (getVisibility() == VISIBLE) {
234be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return;
235be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
236be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        setVisibility(VISIBLE);
237be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        if (ViewCompat.isLaidOut(this)) {
238be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            mImpl.show();
239be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
240be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    }
241be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
242be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    /**
243be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * Hides the button.
244be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     * <p>This method will animate the button hide if the view has already been laid out.</p>
245be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes     */
246be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes    public void hide() {
247be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        if (getVisibility() != VISIBLE) {
248be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return;
249be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
250be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        if (ViewCompat.isLaidOut(this)) {
251be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            mImpl.hide();
252be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        } else {
253be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            setVisibility(GONE);
254d039e3555848f678a2e5363e99026df322d02044Taeho Kim        }
2559840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2569840efe3dbdc7026521da8576574c55120782f6cChris Banes
2579840efe3dbdc7026521da8576574c55120782f6cChris Banes    final int getSizeDimension() {
2589840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (mSize) {
2599840efe3dbdc7026521da8576574c55120782f6cChris Banes            case SIZE_MINI:
2609840efe3dbdc7026521da8576574c55120782f6cChris Banes                return getResources().getDimensionPixelSize(R.dimen.fab_size_mini);
2619840efe3dbdc7026521da8576574c55120782f6cChris Banes            case SIZE_NORMAL:
2629840efe3dbdc7026521da8576574c55120782f6cChris Banes            default:
2639840efe3dbdc7026521da8576574c55120782f6cChris Banes                return getResources().getDimensionPixelSize(R.dimen.fab_size_normal);
2649840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
2659840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2669840efe3dbdc7026521da8576574c55120782f6cChris Banes
2679840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
2689840efe3dbdc7026521da8576574c55120782f6cChris Banes    protected void drawableStateChanged() {
2699840efe3dbdc7026521da8576574c55120782f6cChris Banes        super.drawableStateChanged();
2709840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.onDrawableStateChanged(getDrawableState());
2719840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2729840efe3dbdc7026521da8576574c55120782f6cChris Banes
2739840efe3dbdc7026521da8576574c55120782f6cChris Banes    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
2749840efe3dbdc7026521da8576574c55120782f6cChris Banes    @Override
2759840efe3dbdc7026521da8576574c55120782f6cChris Banes    public void jumpDrawablesToCurrentState() {
2769840efe3dbdc7026521da8576574c55120782f6cChris Banes        super.jumpDrawablesToCurrentState();
2779840efe3dbdc7026521da8576574c55120782f6cChris Banes        mImpl.jumpDrawableToCurrentState();
2789840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
2799840efe3dbdc7026521da8576574c55120782f6cChris Banes
2809840efe3dbdc7026521da8576574c55120782f6cChris Banes    private static int resolveAdjustedSize(int desiredSize, int measureSpec) {
2819840efe3dbdc7026521da8576574c55120782f6cChris Banes        int result = desiredSize;
2829840efe3dbdc7026521da8576574c55120782f6cChris Banes        int specMode = MeasureSpec.getMode(measureSpec);
2839840efe3dbdc7026521da8576574c55120782f6cChris Banes        int specSize = MeasureSpec.getSize(measureSpec);
2849840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (specMode) {
2859840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.UNSPECIFIED:
2869840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Parent says we can be as big as we want. Just don't be larger
2879840efe3dbdc7026521da8576574c55120782f6cChris Banes                // than max size imposed on ourselves.
2889840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = desiredSize;
2899840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
2909840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.AT_MOST:
2919840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Parent says we can be as big as we want, up to specSize.
2929840efe3dbdc7026521da8576574c55120782f6cChris Banes                // Don't be larger than specSize, and don't be larger than
2939840efe3dbdc7026521da8576574c55120782f6cChris Banes                // the max size imposed on ourselves.
2949840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = Math.min(desiredSize, specSize);
2959840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
2969840efe3dbdc7026521da8576574c55120782f6cChris Banes            case MeasureSpec.EXACTLY:
2979840efe3dbdc7026521da8576574c55120782f6cChris Banes                // No choice. Do what we are told.
2989840efe3dbdc7026521da8576574c55120782f6cChris Banes                result = specSize;
2999840efe3dbdc7026521da8576574c55120782f6cChris Banes                break;
3009840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
3019840efe3dbdc7026521da8576574c55120782f6cChris Banes        return result;
3029840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
3039840efe3dbdc7026521da8576574c55120782f6cChris Banes
3049840efe3dbdc7026521da8576574c55120782f6cChris Banes    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
3059840efe3dbdc7026521da8576574c55120782f6cChris Banes        switch (value) {
3069840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 3:
3079840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_OVER;
3089840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 5:
3099840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_IN;
3109840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 9:
3119840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SRC_ATOP;
3129840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 14:
3139840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.MULTIPLY;
3149840efe3dbdc7026521da8576574c55120782f6cChris Banes            case 15:
3159840efe3dbdc7026521da8576574c55120782f6cChris Banes                return PorterDuff.Mode.SCREEN;
3169840efe3dbdc7026521da8576574c55120782f6cChris Banes            default:
3179840efe3dbdc7026521da8576574c55120782f6cChris Banes                return defaultMode;
3189840efe3dbdc7026521da8576574c55120782f6cChris Banes        }
3199840efe3dbdc7026521da8576574c55120782f6cChris Banes    }
32014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes
32114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes    /**
322b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * Behavior designed for use with {@link FloatingActionButton} instances. It's main function
323b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do
324b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     * not cover them.
325b7f9224b1495db47eb8fd813b5912250e900770aChris Banes     */
326b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
327b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is
328b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        // because we can use view translation properties which greatly simplifies the code.
329b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
330b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
331a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private Rect mTmpRect;
332b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        private float mTranslationY;
333b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
334b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
335b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean layoutDependsOn(CoordinatorLayout parent,
336be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                FloatingActionButton child, View dependency) {
337a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            // We're dependent on all SnackbarLayouts (if enabled)
338a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
339b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
340b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
341b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        @Override
342b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                View dependency) {
344a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            if (dependency instanceof Snackbar.SnackbarLayout) {
345a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                updateFabTranslationForSnackbar(parent, child, dependency);
346a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            } else if (dependency instanceof AppBarLayout) {
347be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // If we're depending on an AppBarLayout we will show/hide it automatically
348be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // if the FAB is anchored to the AppBarLayout
349be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                updateFabVisibility(parent, (AppBarLayout) dependency, child);
350be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
351be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return false;
352be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        }
353a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
354be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes        private boolean updateFabVisibility(CoordinatorLayout parent,
355be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                AppBarLayout appBarLayout, FloatingActionButton child) {
356be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final CoordinatorLayout.LayoutParams lp =
357be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                    (CoordinatorLayout.LayoutParams) child.getLayoutParams();
358be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (lp.getAnchorId() != appBarLayout.getId()) {
359be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // The anchor ID doesn't match the dependency, so we won't automatically
360be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // show/hide the FAB
361be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                return false;
362be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
363a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
364be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (mTmpRect == null) {
365be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                mTmpRect = new Rect();
366a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            }
367be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
368be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // First, let's get the visible rect of the dependency
369be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final Rect rect = mTmpRect;
370be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
371be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
372be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
373be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // If the anchor's bottom is below the seam, we'll animate our FAB out
374be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                child.hide();
375be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            } else {
376be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                // Else, we'll animate our FAB back in
377be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                child.show();
378be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
379be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            return true;
380b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
381b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
382a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
383a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                FloatingActionButton fab, View snackbar) {
384be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            if (fab.getVisibility() != View.VISIBLE) {
385be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                return;
386be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
387be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes
388a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final float translationY = getFabTranslationYForSnackbar(parent, fab);
389b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            if (translationY != mTranslationY) {
390b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                // First, cancel any current animation
391b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                ViewCompat.animate(fab).cancel();
392b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
393b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                if (Math.abs(translationY - mTranslationY) == snackbar.getHeight()) {
394b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // If we're travelling by the height of the Snackbar then we probably need to
395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // animate to the value
396a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    ViewCompat.animate(fab)
397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            .translationY(translationY)
398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
399a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            .setListener(null);
400b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                } else {
401b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    // Else we'll set use setTranslationY
402b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                    ViewCompat.setTranslationY(fab, translationY);
403b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
404b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                mTranslationY = translationY;
405b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
406b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
407b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes        private float getFabTranslationYForSnackbar(CoordinatorLayout parent,
409a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                FloatingActionButton fab) {
410b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            float minOffset = 0;
411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            final List<View> dependencies = parent.getDependencies(fab);
412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes            for (int i = 0, z = dependencies.size(); i < z; i++) {
413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                final View view = dependencies.get(i);
414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                    minOffset = Math.min(minOffset,
416a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes                            ViewCompat.getTranslationY(view) - view.getHeight());
417b7f9224b1495db47eb8fd813b5912250e900770aChris Banes                }
418b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            }
419a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes
420b7f9224b1495db47eb8fd813b5912250e900770aChris Banes            return minOffset;
421b7f9224b1495db47eb8fd813b5912250e900770aChris Banes        }
422b7f9224b1495db47eb8fd813b5912250e900770aChris Banes
423e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        @Override
424e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
425e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                int layoutDirection) {
426be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // First, lets make sure that the visibility of the FAB is consistent
427be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            final List<View> dependencies = parent.getDependencies(child);
428be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            for (int i = 0, count = dependencies.size(); i < count; i++) {
429be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                final View dependency = dependencies.get(i);
430be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                if (dependency instanceof AppBarLayout
431be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                        && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
432be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                    break;
433be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes                }
434be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            }
435be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes            // Now let the CoordinatorLayout lay out the FAB
436e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            parent.onLayoutChild(child, layoutDirection);
437e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            // Now offset it if needed
438e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            offsetIfNeeded(parent, child);
439e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            return true;
440e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        }
441e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
442e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        /**
443e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
444e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * offsets our layout position so that we're positioned correctly if we're on one of
445e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         * our parent's edges.
446e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes         */
447e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) {
448e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            final Rect padding = fab.mShadowPadding;
449e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
450e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) {
451e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                final CoordinatorLayout.LayoutParams lp =
452e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                        (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
453e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
454e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                int offsetTB = 0, offsetLR = 0;
455e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
456e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                if (fab.getRight() >= parent.getWidth() - lp.rightMargin) {
457e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the left edge, shift it the right
458e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetLR = padding.right;
459e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                } else if (fab.getLeft() <= lp.leftMargin) {
460e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the left edge, shift it the left
461e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetLR = -padding.left;
462e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                }
463e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                if (fab.getBottom() >= parent.getBottom() - lp.bottomMargin) {
464e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the bottom edge, shift it down
465e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetTB = padding.bottom;
466e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                } else if (fab.getTop() <= lp.topMargin) {
467e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    // If we're on the top edge, shift it up
468e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                    offsetTB = -padding.top;
469e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                }
470e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes
471e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                fab.offsetTopAndBottom(offsetTB);
472e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes                fab.offsetLeftAndRight(offsetLR);
473e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes            }
474e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes        }
475b7f9224b1495db47eb8fd813b5912250e900770aChris Banes    }
4769840efe3dbdc7026521da8576574c55120782f6cChris Banes}
477