FloatingActionButton.java revision 809bb62055ad42b88f3a69308be222801b89fbd9
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; 277a13c8489daca7915623dd673df49de2d1a0bf30Chris Banesimport android.support.annotation.ColorInt; 287a13c8489daca7915623dd673df49de2d1a0bf30Chris Banesimport android.support.annotation.NonNull; 299840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.annotation.Nullable; 309840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.design.R; 31b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat; 329840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.util.AttributeSet; 3314d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.View; 349840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.widget.ImageView; 359840efe3dbdc7026521da8576574c55120782f6cChris Banes 36a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 37b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 389840efe3dbdc7026521da8576574c55120782f6cChris Banes/** 399840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons are used for a special type of promoted action. They are distinguished 4014d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * by a circled icon floating above the UI and have special motion behaviors related to morphing, 419840efe3dbdc7026521da8576574c55120782f6cChris Banes * launching, and the transferring anchor point. 429840efe3dbdc7026521da8576574c55120782f6cChris Banes * 439fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>Floating action buttons come in two sizes: the default and the mini. The size can be 449fb154338a62edc2c57dc036895199d6f1769400Chris Banes * controlled with the {@code fabSize} attribute.</p> 459fb154338a62edc2c57dc036895199d6f1769400Chris Banes * 469fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed 479fb154338a62edc2c57dc036895199d6f1769400Chris Banes * via {@link #setImageDrawable(Drawable)}.</p> 489fb154338a62edc2c57dc036895199d6f1769400Chris Banes * 499fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you 509fb154338a62edc2c57dc036895199d6f1769400Chris Banes * wish to change this at runtime then you can do so via 519fb154338a62edc2c57dc036895199d6f1769400Chris Banes * {@link #setBackgroundTintList(ColorStateList)}.</p> 529fb154338a62edc2c57dc036895199d6f1769400Chris Banes * 539fb154338a62edc2c57dc036895199d6f1769400Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize 549840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 55b7f9224b1495db47eb8fd813b5912250e900770aChris Banes@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class) 569840efe3dbdc7026521da8576574c55120782f6cChris Banespublic class FloatingActionButton extends ImageView { 579840efe3dbdc7026521da8576574c55120782f6cChris Banes 589840efe3dbdc7026521da8576574c55120782f6cChris Banes // These values must match those in the attrs declaration 599840efe3dbdc7026521da8576574c55120782f6cChris Banes private static final int SIZE_MINI = 1; 609840efe3dbdc7026521da8576574c55120782f6cChris Banes private static final int SIZE_NORMAL = 0; 619840efe3dbdc7026521da8576574c55120782f6cChris Banes 629840efe3dbdc7026521da8576574c55120782f6cChris Banes private ColorStateList mBackgroundTint; 639840efe3dbdc7026521da8576574c55120782f6cChris Banes private PorterDuff.Mode mBackgroundTintMode; 649840efe3dbdc7026521da8576574c55120782f6cChris Banes 65cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes private int mBorderWidth; 669840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mRippleColor; 679840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mSize; 689840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mContentPadding; 699840efe3dbdc7026521da8576574c55120782f6cChris Banes 709840efe3dbdc7026521da8576574c55120782f6cChris Banes private final Rect mShadowPadding; 719840efe3dbdc7026521da8576574c55120782f6cChris Banes 729840efe3dbdc7026521da8576574c55120782f6cChris Banes private final FloatingActionButtonImpl mImpl; 739840efe3dbdc7026521da8576574c55120782f6cChris Banes 749840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context) { 759840efe3dbdc7026521da8576574c55120782f6cChris Banes this(context, null); 769840efe3dbdc7026521da8576574c55120782f6cChris Banes } 779840efe3dbdc7026521da8576574c55120782f6cChris Banes 789840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs) { 799840efe3dbdc7026521da8576574c55120782f6cChris Banes this(context, attrs, 0); 809840efe3dbdc7026521da8576574c55120782f6cChris Banes } 819840efe3dbdc7026521da8576574c55120782f6cChris Banes 829840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 839840efe3dbdc7026521da8576574c55120782f6cChris Banes super(context, attrs, defStyleAttr); 849840efe3dbdc7026521da8576574c55120782f6cChris Banes 85809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes ThemeUtils.checkAppCompatTheme(context); 86809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes 879840efe3dbdc7026521da8576574c55120782f6cChris Banes mShadowPadding = new Rect(); 889840efe3dbdc7026521da8576574c55120782f6cChris Banes 899840efe3dbdc7026521da8576574c55120782f6cChris Banes TypedArray a = context.obtainStyledAttributes(attrs, 909840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton, defStyleAttr, 919840efe3dbdc7026521da8576574c55120782f6cChris Banes R.style.Widget_Design_FloatingActionButton); 929840efe3dbdc7026521da8576574c55120782f6cChris Banes Drawable background = a.getDrawable(R.styleable.FloatingActionButton_android_background); 939840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint); 949840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTintMode = parseTintMode(a.getInt( 959840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_backgroundTintMode, -1), null); 969840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0); 979840efe3dbdc7026521da8576574c55120782f6cChris Banes mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_NORMAL); 98cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0); 999840efe3dbdc7026521da8576574c55120782f6cChris Banes final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f); 1009840efe3dbdc7026521da8576574c55120782f6cChris Banes final float pressedTranslationZ = a.getDimension( 1019840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_pressedTranslationZ, 0f); 1029840efe3dbdc7026521da8576574c55120782f6cChris Banes a.recycle(); 1039840efe3dbdc7026521da8576574c55120782f6cChris Banes 1049840efe3dbdc7026521da8576574c55120782f6cChris Banes final ShadowViewDelegate delegate = new ShadowViewDelegate() { 1059840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1069840efe3dbdc7026521da8576574c55120782f6cChris Banes public float getRadius() { 1079840efe3dbdc7026521da8576574c55120782f6cChris Banes return getSizeDimension() / 2f; 1089840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1099840efe3dbdc7026521da8576574c55120782f6cChris Banes 1109840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1119840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setShadowPadding(int left, int top, int right, int bottom) { 1129840efe3dbdc7026521da8576574c55120782f6cChris Banes mShadowPadding.set(left, top, right, bottom); 1139840efe3dbdc7026521da8576574c55120782f6cChris Banes 1149840efe3dbdc7026521da8576574c55120782f6cChris Banes setPadding(left + mContentPadding, top + mContentPadding, 1159840efe3dbdc7026521da8576574c55120782f6cChris Banes right + mContentPadding, bottom + mContentPadding); 1169840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1179840efe3dbdc7026521da8576574c55120782f6cChris Banes 1189840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1199840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundDrawable(Drawable background) { 1209840efe3dbdc7026521da8576574c55120782f6cChris Banes FloatingActionButton.super.setBackgroundDrawable(background); 1219840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1229840efe3dbdc7026521da8576574c55120782f6cChris Banes }; 1239840efe3dbdc7026521da8576574c55120782f6cChris Banes 124be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final int sdk = Build.VERSION.SDK_INT; 125be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (sdk >= 21) { 1269840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl = new FloatingActionButtonLollipop(this, delegate); 127be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } else if (sdk >= 12) { 128be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes mImpl = new FloatingActionButtonHoneycombMr1(this, delegate); 1299840efe3dbdc7026521da8576574c55120782f6cChris Banes } else { 1309840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl = new FloatingActionButtonEclairMr1(this, delegate); 1319840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1329840efe3dbdc7026521da8576574c55120782f6cChris Banes 133a577676a64e5353b8ec927117151aa6be84adf66Chris Banes final int maxContentSize = (int) getResources().getDimension( 134a577676a64e5353b8ec927117151aa6be84adf66Chris Banes R.dimen.design_fab_content_size); 1359840efe3dbdc7026521da8576574c55120782f6cChris Banes mContentPadding = (getSizeDimension() - maxContentSize) / 2; 1369840efe3dbdc7026521da8576574c55120782f6cChris Banes 1379840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setBackgroundDrawable(background, mBackgroundTint, 138cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes mBackgroundTintMode, mRippleColor, mBorderWidth); 1399840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setElevation(elevation); 1409840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setPressedTranslationZ(pressedTranslationZ); 141d039e3555848f678a2e5363e99026df322d02044Taeho Kim 142d039e3555848f678a2e5363e99026df322d02044Taeho Kim setClickable(true); 1439840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1449840efe3dbdc7026521da8576574c55120782f6cChris Banes 1459840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1469840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1479840efe3dbdc7026521da8576574c55120782f6cChris Banes final int preferredSize = getSizeDimension(); 1489840efe3dbdc7026521da8576574c55120782f6cChris Banes 1499840efe3dbdc7026521da8576574c55120782f6cChris Banes final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec); 1509840efe3dbdc7026521da8576574c55120782f6cChris Banes final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec); 1519840efe3dbdc7026521da8576574c55120782f6cChris Banes 1529840efe3dbdc7026521da8576574c55120782f6cChris Banes // As we want to stay circular, we set both dimensions to be the 1539840efe3dbdc7026521da8576574c55120782f6cChris Banes // smallest resolved dimension 1549840efe3dbdc7026521da8576574c55120782f6cChris Banes final int d = Math.min(w, h); 1559840efe3dbdc7026521da8576574c55120782f6cChris Banes 1569840efe3dbdc7026521da8576574c55120782f6cChris Banes // We add the shadow's padding to the measured dimension 1579840efe3dbdc7026521da8576574c55120782f6cChris Banes setMeasuredDimension( 1589840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.left + mShadowPadding.right, 1599840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.top + mShadowPadding.bottom); 1609840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1619840efe3dbdc7026521da8576574c55120782f6cChris Banes 1629840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1639840efe3dbdc7026521da8576574c55120782f6cChris Banes * Set the ripple color for this {@link FloatingActionButton}. 1649840efe3dbdc7026521da8576574c55120782f6cChris Banes * <p> 1659840efe3dbdc7026521da8576574c55120782f6cChris Banes * When running on devices with KitKat or below, we draw a fill rather than a ripple. 1669840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1679840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param color ARGB color to use for the ripple. 1689840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1697a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes public void setRippleColor(@ColorInt int color) { 1709840efe3dbdc7026521da8576574c55120782f6cChris Banes if (mRippleColor != color) { 1719840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = color; 1729840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.setRippleColor(color); 1739840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1749840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1759840efe3dbdc7026521da8576574c55120782f6cChris Banes 1769840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1779840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the tint applied to the background drawable, if specified. 1789840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1799840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the tint applied to the background drawable 1809840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintList(ColorStateList) 1819840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1829840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 1839840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1849840efe3dbdc7026521da8576574c55120782f6cChris Banes public ColorStateList getBackgroundTintList() { 1859840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTint; 1869840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1879840efe3dbdc7026521da8576574c55120782f6cChris Banes 1889840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 1899840efe3dbdc7026521da8576574c55120782f6cChris Banes * Applies a tint to the background drawable. Does not modify the current tint 1909840efe3dbdc7026521da8576574c55120782f6cChris Banes * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 1919840efe3dbdc7026521da8576574c55120782f6cChris Banes * 1929840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tint the tint to apply, may be {@code null} to clear tint 1939840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 1949840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintList(@Nullable ColorStateList tint) { 1957a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes if (mBackgroundTint != tint) { 1967a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mBackgroundTint = tint; 1977a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mImpl.setBackgroundTintList(tint); 1987a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes } 1999840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2009840efe3dbdc7026521da8576574c55120782f6cChris Banes 2019840efe3dbdc7026521da8576574c55120782f6cChris Banes 2029840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2039840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the blending mode used to apply the tint to the background 2049840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable, if specified. 2059840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2069840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the blending mode used to apply the tint to the background 2079840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable 2089840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintMode(PorterDuff.Mode) 2099840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2109840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 2119840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2129840efe3dbdc7026521da8576574c55120782f6cChris Banes public PorterDuff.Mode getBackgroundTintMode() { 2139840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTintMode; 2149840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2159840efe3dbdc7026521da8576574c55120782f6cChris Banes 2169840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2179840efe3dbdc7026521da8576574c55120782f6cChris Banes * Specifies the blending mode used to apply the tint specified by 2189840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@link #setBackgroundTintList(ColorStateList)}} to the background 2199840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 2209840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2219840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tintMode the blending mode used to apply the tint, may be 2229840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@code null} to clear tint 2239840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2249840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 2257a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes if (mBackgroundTintMode != tintMode) { 2267a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mBackgroundTintMode = tintMode; 2277a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mImpl.setBackgroundTintMode(tintMode); 2287a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes } 2299840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2309840efe3dbdc7026521da8576574c55120782f6cChris Banes 2319840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2327a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes public void setBackgroundDrawable(@NonNull Drawable background) { 233d039e3555848f678a2e5363e99026df322d02044Taeho Kim if (mImpl != null) { 234d039e3555848f678a2e5363e99026df322d02044Taeho Kim mImpl.setBackgroundDrawable( 235be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth); 236be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 237be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 238be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 239be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes /** 240be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * Shows the button. 241be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * <p>This method will animate it the button show if the view has already been laid out.</p> 242be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes */ 243be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes public void show() { 24439d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes mImpl.show(); 245be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 246be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 247be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes /** 248be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * Hides the button. 249be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * <p>This method will animate the button hide if the view has already been laid out.</p> 250be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes */ 251be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes public void hide() { 25239d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes mImpl.hide(); 2539840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2549840efe3dbdc7026521da8576574c55120782f6cChris Banes 2559840efe3dbdc7026521da8576574c55120782f6cChris Banes final int getSizeDimension() { 2569840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (mSize) { 2579840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_MINI: 258a577676a64e5353b8ec927117151aa6be84adf66Chris Banes return getResources().getDimensionPixelSize(R.dimen.design_fab_size_mini); 2599840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_NORMAL: 2609840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 261a577676a64e5353b8ec927117151aa6be84adf66Chris Banes return getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal); 2629840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2639840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2649840efe3dbdc7026521da8576574c55120782f6cChris Banes 2659840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2669840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void drawableStateChanged() { 2679840efe3dbdc7026521da8576574c55120782f6cChris Banes super.drawableStateChanged(); 2689840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.onDrawableStateChanged(getDrawableState()); 2699840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2709840efe3dbdc7026521da8576574c55120782f6cChris Banes 2719840efe3dbdc7026521da8576574c55120782f6cChris Banes @TargetApi(Build.VERSION_CODES.HONEYCOMB) 2729840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2739840efe3dbdc7026521da8576574c55120782f6cChris Banes public void jumpDrawablesToCurrentState() { 2749840efe3dbdc7026521da8576574c55120782f6cChris Banes super.jumpDrawablesToCurrentState(); 2759840efe3dbdc7026521da8576574c55120782f6cChris Banes mImpl.jumpDrawableToCurrentState(); 2769840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2779840efe3dbdc7026521da8576574c55120782f6cChris Banes 2789840efe3dbdc7026521da8576574c55120782f6cChris Banes private static int resolveAdjustedSize(int desiredSize, int measureSpec) { 2799840efe3dbdc7026521da8576574c55120782f6cChris Banes int result = desiredSize; 2809840efe3dbdc7026521da8576574c55120782f6cChris Banes int specMode = MeasureSpec.getMode(measureSpec); 2819840efe3dbdc7026521da8576574c55120782f6cChris Banes int specSize = MeasureSpec.getSize(measureSpec); 2829840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (specMode) { 2839840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.UNSPECIFIED: 2849840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want. Just don't be larger 2859840efe3dbdc7026521da8576574c55120782f6cChris Banes // than max size imposed on ourselves. 2869840efe3dbdc7026521da8576574c55120782f6cChris Banes result = desiredSize; 2879840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2889840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.AT_MOST: 2899840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want, up to specSize. 2909840efe3dbdc7026521da8576574c55120782f6cChris Banes // Don't be larger than specSize, and don't be larger than 2919840efe3dbdc7026521da8576574c55120782f6cChris Banes // the max size imposed on ourselves. 2929840efe3dbdc7026521da8576574c55120782f6cChris Banes result = Math.min(desiredSize, specSize); 2939840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2949840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.EXACTLY: 2959840efe3dbdc7026521da8576574c55120782f6cChris Banes // No choice. Do what we are told. 2969840efe3dbdc7026521da8576574c55120782f6cChris Banes result = specSize; 2979840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 2989840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2999840efe3dbdc7026521da8576574c55120782f6cChris Banes return result; 3009840efe3dbdc7026521da8576574c55120782f6cChris Banes } 3019840efe3dbdc7026521da8576574c55120782f6cChris Banes 3029840efe3dbdc7026521da8576574c55120782f6cChris Banes static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { 3039840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (value) { 3049840efe3dbdc7026521da8576574c55120782f6cChris Banes case 3: 3059840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_OVER; 3069840efe3dbdc7026521da8576574c55120782f6cChris Banes case 5: 3079840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_IN; 3089840efe3dbdc7026521da8576574c55120782f6cChris Banes case 9: 3099840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_ATOP; 3109840efe3dbdc7026521da8576574c55120782f6cChris Banes case 14: 3119840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.MULTIPLY; 3129840efe3dbdc7026521da8576574c55120782f6cChris Banes case 15: 3139840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SCREEN; 3149840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 3159840efe3dbdc7026521da8576574c55120782f6cChris Banes return defaultMode; 3169840efe3dbdc7026521da8576574c55120782f6cChris Banes } 3179840efe3dbdc7026521da8576574c55120782f6cChris Banes } 31814d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 31914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes /** 320b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Behavior designed for use with {@link FloatingActionButton} instances. It's main function 321b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do 322b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * not cover them. 323b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 324b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> { 325b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is 326b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // because we can use view translation properties which greatly simplifies the code. 327b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; 328b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 329a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private Rect mTmpRect; 330b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 331b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 332b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean layoutDependsOn(CoordinatorLayout parent, 333be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes FloatingActionButton child, View dependency) { 334a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're dependent on all SnackbarLayouts (if enabled) 335a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; 336b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 337b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 338b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 339b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, 340a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 341a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependency instanceof Snackbar.SnackbarLayout) { 342a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes updateFabTranslationForSnackbar(parent, child, dependency); 343a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (dependency instanceof AppBarLayout) { 344be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // If we're depending on an AppBarLayout we will show/hide it automatically 345be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // if the FAB is anchored to the AppBarLayout 346be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes updateFabVisibility(parent, (AppBarLayout) dependency, child); 347be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 348be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return false; 349be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 350a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 3510b00489086b22c9688c8c04990dadf54489d45e3Chris Banes @Override 3520b00489086b22c9688c8c04990dadf54489d45e3Chris Banes public void onDependentViewRemoved(CoordinatorLayout parent, FloatingActionButton child, 3530b00489086b22c9688c8c04990dadf54489d45e3Chris Banes View dependency) { 3540b00489086b22c9688c8c04990dadf54489d45e3Chris Banes if (dependency instanceof Snackbar.SnackbarLayout) { 3550b00489086b22c9688c8c04990dadf54489d45e3Chris Banes // If the removed view is a SnackbarLayout, we will animate back to our normal 3560b00489086b22c9688c8c04990dadf54489d45e3Chris Banes // position 35739d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes if (ViewCompat.getTranslationY(child) != 0f) { 35839d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes ViewCompat.animate(child) 35939d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .translationY(0f) 36039d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .scaleX(1f) 36139d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .scaleY(1f) 36239d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .alpha(1f) 36339d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR) 36439d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes .setListener(null); 36539d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes } 3660b00489086b22c9688c8c04990dadf54489d45e3Chris Banes } 3670b00489086b22c9688c8c04990dadf54489d45e3Chris Banes } 3680b00489086b22c9688c8c04990dadf54489d45e3Chris Banes 369be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes private boolean updateFabVisibility(CoordinatorLayout parent, 370be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes AppBarLayout appBarLayout, FloatingActionButton child) { 371be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final CoordinatorLayout.LayoutParams lp = 372be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes (CoordinatorLayout.LayoutParams) child.getLayoutParams(); 373be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (lp.getAnchorId() != appBarLayout.getId()) { 374be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // The anchor ID doesn't match the dependency, so we won't automatically 375be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // show/hide the FAB 376be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return false; 377be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 378a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 379be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (mTmpRect == null) { 380be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes mTmpRect = new Rect(); 381a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 382be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 383be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // First, let's get the visible rect of the dependency 384be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final Rect rect = mTmpRect; 385be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect); 386be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 387be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) { 388be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // If the anchor's bottom is below the seam, we'll animate our FAB out 389be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes child.hide(); 390be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } else { 391be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // Else, we'll animate our FAB back in 392be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes child.show(); 393be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 394be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return true; 395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 396b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 397a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private void updateFabTranslationForSnackbar(CoordinatorLayout parent, 398a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes FloatingActionButton fab, View snackbar) { 399be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (fab.getVisibility() != View.VISIBLE) { 400be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return; 401be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 402be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 403a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final float translationY = getFabTranslationYForSnackbar(parent, fab); 40439d1d2d33cdd490d8b9f68d3300725035c1e0c56Chris Banes ViewCompat.setTranslationY(fab, translationY); 405b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 406b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 407a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private float getFabTranslationYForSnackbar(CoordinatorLayout parent, 408a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes FloatingActionButton fab) { 409b7f9224b1495db47eb8fd813b5912250e900770aChris Banes float minOffset = 0; 410a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(fab); 411a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = dependencies.size(); i < z; i++) { 412a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View view = dependencies.get(i); 413a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) { 414a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes minOffset = Math.min(minOffset, 415a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ViewCompat.getTranslationY(view) - view.getHeight()); 416b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 417b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 418a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 419b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return minOffset; 420b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 421b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 422e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes @Override 423e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child, 424e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes int layoutDirection) { 425be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // First, lets make sure that the visibility of the FAB is consistent 426be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final List<View> dependencies = parent.getDependencies(child); 427be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes for (int i = 0, count = dependencies.size(); i < count; i++) { 428be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final View dependency = dependencies.get(i); 429be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (dependency instanceof AppBarLayout 430be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes && updateFabVisibility(parent, (AppBarLayout) dependency, child)) { 431be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes break; 432be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 433be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 434be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // Now let the CoordinatorLayout lay out the FAB 435e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes parent.onLayoutChild(child, layoutDirection); 436e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // Now offset it if needed 437e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetIfNeeded(parent, child); 438e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes return true; 439e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 440e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 441e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes /** 442e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method 443e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * offsets our layout position so that we're positioned correctly if we're on one of 444e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * our parent's edges. 445e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes */ 446e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) { 447e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes final Rect padding = fab.mShadowPadding; 448e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 449e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) { 450e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes final CoordinatorLayout.LayoutParams lp = 451e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); 452e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 453e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes int offsetTB = 0, offsetLR = 0; 454e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 455e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (fab.getRight() >= parent.getWidth() - lp.rightMargin) { 456e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the left edge, shift it the right 457e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetLR = padding.right; 458e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } else if (fab.getLeft() <= lp.leftMargin) { 459e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the left edge, shift it the left 460e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetLR = -padding.left; 461e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 462e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (fab.getBottom() >= parent.getBottom() - lp.bottomMargin) { 463e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the bottom edge, shift it down 464e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetTB = padding.bottom; 465e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } else if (fab.getTop() <= lp.topMargin) { 466e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the top edge, shift it up 467e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetTB = -padding.top; 468e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 469e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 470e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes fab.offsetTopAndBottom(offsetTB); 471e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes fab.offsetLeftAndRight(offsetLR); 472e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 473e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 474b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 4759840efe3dbdc7026521da8576574c55120782f6cChris Banes} 476