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; 22a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banesimport android.content.res.Resources; 239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.TypedArray; 249840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff; 259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.Rect; 269840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable; 279840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.os.Build; 287a13c8489daca7915623dd673df49de2d1a0bf30Chris Banesimport android.support.annotation.ColorInt; 291711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.annotation.DrawableRes; 30a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banesimport android.support.annotation.IntDef; 31d9cbe69a6661315238d856abc22578d03666f63bChris Banesimport android.support.annotation.NonNull; 329840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.annotation.Nullable; 339840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.support.design.R; 340ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Weiimport android.support.design.widget.FloatingActionButtonImpl.InternalVisibilityChangedListener; 35a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banesimport android.support.v4.content.res.ConfigurationHelper; 36b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat; 371711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.v7.widget.AppCompatDrawableManager; 381711e8729c1b901b73f530e87b7c9cc9370f33beChris Banesimport android.support.v7.widget.AppCompatImageHelper; 399840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.util.AttributeSet; 40097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.util.Log; 41d5d3213d80be767482a89bea6d073006582e2bccChris Banesimport android.view.MotionEvent; 4214d064edb3e4a16a3b90a4a850560177bea1e60dChris Banesimport android.view.View; 439840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.widget.ImageView; 449840efe3dbdc7026521da8576574c55120782f6cChris Banes 45a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banesimport java.lang.annotation.Retention; 46a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banesimport java.lang.annotation.RetentionPolicy; 47a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banesimport java.util.List; 48b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 499840efe3dbdc7026521da8576574c55120782f6cChris Banes/** 509840efe3dbdc7026521da8576574c55120782f6cChris Banes * Floating action buttons are used for a special type of promoted action. They are distinguished 5114d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes * by a circled icon floating above the UI and have special motion behaviors related to morphing, 529840efe3dbdc7026521da8576574c55120782f6cChris Banes * launching, and the transferring anchor point. 539840efe3dbdc7026521da8576574c55120782f6cChris Banes * 549fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>Floating action buttons come in two sizes: the default and the mini. The size can be 559fb154338a62edc2c57dc036895199d6f1769400Chris Banes * controlled with the {@code fabSize} attribute.</p> 569fb154338a62edc2c57dc036895199d6f1769400Chris Banes * 579fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>As this class descends from {@link ImageView}, you can control the icon which is displayed 589fb154338a62edc2c57dc036895199d6f1769400Chris Banes * via {@link #setImageDrawable(Drawable)}.</p> 599fb154338a62edc2c57dc036895199d6f1769400Chris Banes * 609fb154338a62edc2c57dc036895199d6f1769400Chris Banes * <p>The background color of this view defaults to the your theme's {@code colorAccent}. If you 619fb154338a62edc2c57dc036895199d6f1769400Chris Banes * wish to change this at runtime then you can do so via 629fb154338a62edc2c57dc036895199d6f1769400Chris Banes * {@link #setBackgroundTintList(ColorStateList)}.</p> 639840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 64b7f9224b1495db47eb8fd813b5912250e900770aChris Banes@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class) 65fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banespublic class FloatingActionButton extends VisibilityAwareImageButton { 669840efe3dbdc7026521da8576574c55120782f6cChris Banes 67097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes private static final String LOG_TAG = "FloatingActionButton"; 68097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes 690ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei /** 700ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * Callback to be invoked when the visibility of a FloatingActionButton changes. 710ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei */ 720ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public abstract static class OnVisibilityChangedListener { 730ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei /** 740ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * Called when a FloatingActionButton has been 750ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * {@link #show(OnVisibilityChangedListener) shown}. 760ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * 770ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * @param fab the FloatingActionButton that was shown. 780ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei */ 790ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onShown(FloatingActionButton fab) {} 800ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 810ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei /** 820ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * Called when a FloatingActionButton has been 830ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * {@link #hide(OnVisibilityChangedListener) hidden}. 840ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * 850ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * @param fab the FloatingActionButton that was hidden. 860ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei */ 870ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onHidden(FloatingActionButton fab) {} 880ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 890ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 909840efe3dbdc7026521da8576574c55120782f6cChris Banes // These values must match those in the attrs declaration 91a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 92a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 93a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * The mini sized button. Will always been smaller than {@link #SIZE_NORMAL}. 94a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 95a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @see #setSize(int) 96a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 97a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public static final int SIZE_MINI = 1; 98a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 99a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 100a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * The normal sized button. Will always been larger than {@link #SIZE_MINI}. 101a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 102a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @see #setSize(int) 103a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 104a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public static final int SIZE_NORMAL = 0; 105a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 106a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 107a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * Size which will change based on the window size. For small sized windows 108a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * (largest screen dimension < 470dp) this will select a small sized button, and for 109a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * larger sized windows it will select a larger size. 110a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 111a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @see #setSize(int) 112a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 113a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public static final int SIZE_AUTO = -1; 114a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 115a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 116a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * The switch point for the largest screen edge where SIZE_AUTO switches from mini to normal. 117a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 118a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes private static final int AUTO_MINI_LARGEST_SCREEN_WIDTH = 470; 119a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 120a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** @hide */ 121a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes @Retention(RetentionPolicy.SOURCE) 122a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes @IntDef({SIZE_MINI, SIZE_NORMAL, SIZE_AUTO}) 123a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public @interface Size {} 1249840efe3dbdc7026521da8576574c55120782f6cChris Banes 1259840efe3dbdc7026521da8576574c55120782f6cChris Banes private ColorStateList mBackgroundTint; 1269840efe3dbdc7026521da8576574c55120782f6cChris Banes private PorterDuff.Mode mBackgroundTintMode; 1279840efe3dbdc7026521da8576574c55120782f6cChris Banes 128cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes private int mBorderWidth; 1299840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mRippleColor; 1309840efe3dbdc7026521da8576574c55120782f6cChris Banes private int mSize; 131d9cbe69a6661315238d856abc22578d03666f63bChris Banes private int mImagePadding; 132a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes private int mMaxImageSize; 1339840efe3dbdc7026521da8576574c55120782f6cChris Banes 1346d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes private boolean mCompatPadding; 135cdc736866534c604c4015c78371ade52bb6d52dfChris Banes private final Rect mShadowPadding = new Rect(); 136df23c413315751774dd83a1d930ae6e83bc21d55Chris Banes private final Rect mTouchArea = new Rect(); 1379840efe3dbdc7026521da8576574c55120782f6cChris Banes 1381711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes private AppCompatImageHelper mImageHelper; 1391711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes 140d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes private FloatingActionButtonImpl mImpl; 1419840efe3dbdc7026521da8576574c55120782f6cChris Banes 1429840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context) { 1439840efe3dbdc7026521da8576574c55120782f6cChris Banes this(context, null); 1449840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1459840efe3dbdc7026521da8576574c55120782f6cChris Banes 1469840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs) { 147b3ba94bf8b5cdae24e5a09a83813d72f2e2d8c1aChris Banes this(context, attrs, 0); 1489840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1499840efe3dbdc7026521da8576574c55120782f6cChris Banes 1509840efe3dbdc7026521da8576574c55120782f6cChris Banes public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) { 1519840efe3dbdc7026521da8576574c55120782f6cChris Banes super(context, attrs, defStyleAttr); 1529840efe3dbdc7026521da8576574c55120782f6cChris Banes 153809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes ThemeUtils.checkAppCompatTheme(context); 154809bb62055ad42b88f3a69308be222801b89fbd9Chris Banes 1559840efe3dbdc7026521da8576574c55120782f6cChris Banes TypedArray a = context.obtainStyledAttributes(attrs, 1569840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton, defStyleAttr, 1579840efe3dbdc7026521da8576574c55120782f6cChris Banes R.style.Widget_Design_FloatingActionButton); 1589840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint); 1599840efe3dbdc7026521da8576574c55120782f6cChris Banes mBackgroundTintMode = parseTintMode(a.getInt( 1609840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_backgroundTintMode, -1), null); 1619840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0); 162a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_AUTO); 163cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes mBorderWidth = a.getDimensionPixelSize(R.styleable.FloatingActionButton_borderWidth, 0); 1649840efe3dbdc7026521da8576574c55120782f6cChris Banes final float elevation = a.getDimension(R.styleable.FloatingActionButton_elevation, 0f); 1659840efe3dbdc7026521da8576574c55120782f6cChris Banes final float pressedTranslationZ = a.getDimension( 1669840efe3dbdc7026521da8576574c55120782f6cChris Banes R.styleable.FloatingActionButton_pressedTranslationZ, 0f); 1676d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes mCompatPadding = a.getBoolean(R.styleable.FloatingActionButton_useCompatPadding, false); 1689840efe3dbdc7026521da8576574c55120782f6cChris Banes a.recycle(); 1699840efe3dbdc7026521da8576574c55120782f6cChris Banes 1701711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes mImageHelper = new AppCompatImageHelper(this, AppCompatDrawableManager.get()); 1711711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes mImageHelper.loadFromAttributes(attrs, defStyleAttr); 1721711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes 173a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes mMaxImageSize = (int) getResources().getDimension(R.dimen.design_fab_image_size); 1749840efe3dbdc7026521da8576574c55120782f6cChris Banes 175d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode, 176097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes mRippleColor, mBorderWidth); 177d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setElevation(elevation); 178d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setPressedTranslationZ(pressedTranslationZ); 1799840efe3dbdc7026521da8576574c55120782f6cChris Banes } 1809840efe3dbdc7026521da8576574c55120782f6cChris Banes 1819840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 1829840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 1839840efe3dbdc7026521da8576574c55120782f6cChris Banes final int preferredSize = getSizeDimension(); 1849840efe3dbdc7026521da8576574c55120782f6cChris Banes 185a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes mImagePadding = (preferredSize - mMaxImageSize) / 2; 186a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes getImpl().updatePadding(); 187a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 1889840efe3dbdc7026521da8576574c55120782f6cChris Banes final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec); 1899840efe3dbdc7026521da8576574c55120782f6cChris Banes final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec); 1909840efe3dbdc7026521da8576574c55120782f6cChris Banes 1919840efe3dbdc7026521da8576574c55120782f6cChris Banes // As we want to stay circular, we set both dimensions to be the 1929840efe3dbdc7026521da8576574c55120782f6cChris Banes // smallest resolved dimension 1939840efe3dbdc7026521da8576574c55120782f6cChris Banes final int d = Math.min(w, h); 1949840efe3dbdc7026521da8576574c55120782f6cChris Banes 1959840efe3dbdc7026521da8576574c55120782f6cChris Banes // We add the shadow's padding to the measured dimension 1969840efe3dbdc7026521da8576574c55120782f6cChris Banes setMeasuredDimension( 1979840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.left + mShadowPadding.right, 1989840efe3dbdc7026521da8576574c55120782f6cChris Banes d + mShadowPadding.top + mShadowPadding.bottom); 1999840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2009840efe3dbdc7026521da8576574c55120782f6cChris Banes 2019840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2029840efe3dbdc7026521da8576574c55120782f6cChris Banes * Set the ripple color for this {@link FloatingActionButton}. 2039840efe3dbdc7026521da8576574c55120782f6cChris Banes * <p> 2049840efe3dbdc7026521da8576574c55120782f6cChris Banes * When running on devices with KitKat or below, we draw a fill rather than a ripple. 2059840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2069840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param color ARGB color to use for the ripple. 2076d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 2086d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_rippleColor 2099840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2107a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes public void setRippleColor(@ColorInt int color) { 2119840efe3dbdc7026521da8576574c55120782f6cChris Banes if (mRippleColor != color) { 2129840efe3dbdc7026521da8576574c55120782f6cChris Banes mRippleColor = color; 213d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setRippleColor(color); 2149840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2159840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2169840efe3dbdc7026521da8576574c55120782f6cChris Banes 2179840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2189840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the tint applied to the background drawable, if specified. 2199840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2209840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the tint applied to the background drawable 2219840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintList(ColorStateList) 2229840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2239840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 2249840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2259840efe3dbdc7026521da8576574c55120782f6cChris Banes public ColorStateList getBackgroundTintList() { 2269840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTint; 2279840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2289840efe3dbdc7026521da8576574c55120782f6cChris Banes 2299840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2309840efe3dbdc7026521da8576574c55120782f6cChris Banes * Applies a tint to the background drawable. Does not modify the current tint 2319840efe3dbdc7026521da8576574c55120782f6cChris Banes * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 2329840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2339840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tint the tint to apply, may be {@code null} to clear tint 2349840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2359840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintList(@Nullable ColorStateList tint) { 2367a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes if (mBackgroundTint != tint) { 2377a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mBackgroundTint = tint; 238d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setBackgroundTintList(tint); 2397a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes } 2409840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2419840efe3dbdc7026521da8576574c55120782f6cChris Banes 2429840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2439840efe3dbdc7026521da8576574c55120782f6cChris Banes * Return the blending mode used to apply the tint to the background 2449840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable, if specified. 2459840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2469840efe3dbdc7026521da8576574c55120782f6cChris Banes * @return the blending mode used to apply the tint to the background 2479840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable 2489840efe3dbdc7026521da8576574c55120782f6cChris Banes * @see #setBackgroundTintMode(PorterDuff.Mode) 2499840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2509840efe3dbdc7026521da8576574c55120782f6cChris Banes @Nullable 2519840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 2529840efe3dbdc7026521da8576574c55120782f6cChris Banes public PorterDuff.Mode getBackgroundTintMode() { 2539840efe3dbdc7026521da8576574c55120782f6cChris Banes return mBackgroundTintMode; 2549840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2559840efe3dbdc7026521da8576574c55120782f6cChris Banes 2569840efe3dbdc7026521da8576574c55120782f6cChris Banes /** 2579840efe3dbdc7026521da8576574c55120782f6cChris Banes * Specifies the blending mode used to apply the tint specified by 2589840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@link #setBackgroundTintList(ColorStateList)}} to the background 2599840efe3dbdc7026521da8576574c55120782f6cChris Banes * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}. 2609840efe3dbdc7026521da8576574c55120782f6cChris Banes * 2619840efe3dbdc7026521da8576574c55120782f6cChris Banes * @param tintMode the blending mode used to apply the tint, may be 2629840efe3dbdc7026521da8576574c55120782f6cChris Banes * {@code null} to clear tint 2639840efe3dbdc7026521da8576574c55120782f6cChris Banes */ 2649840efe3dbdc7026521da8576574c55120782f6cChris Banes public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 2657a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes if (mBackgroundTintMode != tintMode) { 2667a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes mBackgroundTintMode = tintMode; 267d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setBackgroundTintMode(tintMode); 2687a13c8489daca7915623dd673df49de2d1a0bf30Chris Banes } 2699840efe3dbdc7026521da8576574c55120782f6cChris Banes } 2709840efe3dbdc7026521da8576574c55120782f6cChris Banes 2719840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 272097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes public void setBackgroundDrawable(Drawable background) { 273097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes Log.i(LOG_TAG, "Setting a custom background is not supported."); 274097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes } 275097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes 276097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes @Override 277097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes public void setBackgroundResource(int resid) { 278097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes Log.i(LOG_TAG, "Setting a custom background is not supported."); 279097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes } 280097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes 281097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes @Override 282097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes public void setBackgroundColor(int color) { 283097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes Log.i(LOG_TAG, "Setting a custom background is not supported."); 284be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 285be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 2861711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes @Override 2871711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes public void setImageResource(@DrawableRes int resId) { 2881711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes // Intercept this call and instead retrieve the Drawable via the image helper 2891711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes mImageHelper.setImageResource(resId); 2901711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes } 2911711e8729c1b901b73f530e87b7c9cc9370f33beChris Banes 292be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes /** 293be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * Shows the button. 2948c05e5f52fbc790b745e768398d9e69d6b9d9ee1Chris Banes * <p>This method will animate the button show if the view has already been laid out.</p> 295be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes */ 296be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes public void show() { 297fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes show(null); 2980ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 2990ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 3000ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei /** 3010ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * Shows the button. 3028c05e5f52fbc790b745e768398d9e69d6b9d9ee1Chris Banes * <p>This method will animate the button show if the view has already been laid out.</p> 3030ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * 3040ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * @param listener the listener to notify when this view is shown 3050ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei */ 3060ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void show(@Nullable final OnVisibilityChangedListener listener) { 307fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes show(listener, true); 308fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes } 309fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes 310fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes private void show(OnVisibilityChangedListener listener, boolean fromUser) { 311d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().show(wrapOnVisibilityChangedListener(listener), fromUser); 312be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 313be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 314be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes /** 315be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * Hides the button. 316be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes * <p>This method will animate the button hide if the view has already been laid out.</p> 317be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes */ 318be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes public void hide() { 319fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes hide(null); 3200ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 3210ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 3220ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei /** 3230ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * Hides the button. 3240ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * <p>This method will animate the button hide if the view has already been laid out.</p> 3250ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * 3260ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei * @param listener the listener to notify when this view is hidden 3270ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei */ 3280ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void hide(@Nullable OnVisibilityChangedListener listener) { 329fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes hide(listener, true); 330fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes } 331fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes 332fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes private void hide(@Nullable OnVisibilityChangedListener listener, boolean fromUser) { 333d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().hide(wrapOnVisibilityChangedListener(listener), fromUser); 3340ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 3350ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 3366d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes /** 3376d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * Set whether FloatingActionButton should add inner padding on platforms Lollipop and after, 3386d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * to ensure consistent dimensions on all platforms. 3396d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 3406d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @param useCompatPadding true if FloatingActionButton is adding inner padding on platforms 3416d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * Lollipop and after, to ensure consistent dimensions on all platforms. 3426d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 3436d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding 3446d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @see #getUseCompatPadding() 3456d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes */ 3466d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes public void setUseCompatPadding(boolean useCompatPadding) { 3476d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes if (mCompatPadding != useCompatPadding) { 3486d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes mCompatPadding = useCompatPadding; 349d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().onCompatShadowChanged(); 3506d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 3516d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 3526d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 3536d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes /** 3546d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * Returns whether FloatingActionButton will add inner padding on platforms Lollipop and after. 3556d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 3566d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @return true if FloatingActionButton is adding inner padding on platforms Lollipop and after, 3576d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * to ensure consistent dimensions on all platforms. 3586d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 3596d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_useCompatPadding 3606d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @see #setUseCompatPadding(boolean) 3616d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes */ 3626d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes public boolean getUseCompatPadding() { 3636d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes return mCompatPadding; 3646d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 3656d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 366a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 367a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * Sets the size of the button. 368a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 369a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * <p>The options relate to the options available on the material design specification. 370a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * {@link #SIZE_NORMAL} is larger than {@link #SIZE_MINI}. {@link #SIZE_AUTO} will choose 371a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * an appropriate size based on the screen size.</p> 372a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 373a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @param size one of {@link #SIZE_NORMAL}, {@link #SIZE_MINI} or {@link #SIZE_AUTO} 374a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 375a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_fabSize 376a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 377a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public void setSize(@Size int size) { 378a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes if (size != mSize) { 379a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes mSize = size; 380a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes requestLayout(); 381a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes } 382a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes } 383a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 384a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes /** 385a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * Returns the chosen size for this button. 386a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * 38756f0ad8d4fa234d2801971042ff1d71aba960e37Chris Banes * @return one of {@link #SIZE_NORMAL}, {@link #SIZE_MINI} or {@link #SIZE_AUTO} 388a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes * @see #setSize(int) 389a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes */ 390a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes @Size 391a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes public int getSize() { 392a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes return mSize; 393a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes } 394a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 3950ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei @Nullable 3960ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei private InternalVisibilityChangedListener wrapOnVisibilityChangedListener( 3970ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei @Nullable final OnVisibilityChangedListener listener) { 3980ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei if (listener == null) { 3990ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei return null; 4000ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 4010ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 4020ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei return new InternalVisibilityChangedListener() { 4030ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei @Override 4040ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onShown() { 4050ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei listener.onShown(FloatingActionButton.this); 4060ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 4070ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 4080ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei @Override 4090ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onHidden() { 4100ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei listener.onHidden(FloatingActionButton.this); 4110ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 4120ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei }; 4139840efe3dbdc7026521da8576574c55120782f6cChris Banes } 4149840efe3dbdc7026521da8576574c55120782f6cChris Banes 415a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes private int getSizeDimension() { 416a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes return getSizeDimension(mSize); 417a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes } 418a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes 419a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes private int getSizeDimension(@Size final int size) { 420a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes final Resources res = getResources(); 421a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes switch (size) { 422a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes case SIZE_AUTO: 423a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes // If we're set to auto, grab the size from resources and refresh 424a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes final int width = ConfigurationHelper.getScreenWidthDp(res); 425a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes final int height = ConfigurationHelper.getScreenHeightDp(res); 426a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes return Math.max(width, height) < AUTO_MINI_LARGEST_SCREEN_WIDTH 427a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes ? getSizeDimension(SIZE_MINI) 428a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes : getSizeDimension(SIZE_NORMAL); 4299840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_MINI: 430a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes return res.getDimensionPixelSize(R.dimen.design_fab_size_mini); 4319840efe3dbdc7026521da8576574c55120782f6cChris Banes case SIZE_NORMAL: 4329840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 433a1de3eef9bb5ef90d00a23c65f13e1fc83254455Chris Banes return res.getDimensionPixelSize(R.dimen.design_fab_size_normal); 4349840efe3dbdc7026521da8576574c55120782f6cChris Banes } 4359840efe3dbdc7026521da8576574c55120782f6cChris Banes } 4369840efe3dbdc7026521da8576574c55120782f6cChris Banes 4379840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 438d9770e12c8ff2d4417700492c6616572be897e93Chris Banes protected void onAttachedToWindow() { 439d9770e12c8ff2d4417700492c6616572be897e93Chris Banes super.onAttachedToWindow(); 440d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().onAttachedToWindow(); 441d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 442d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 443d9770e12c8ff2d4417700492c6616572be897e93Chris Banes @Override 444d9770e12c8ff2d4417700492c6616572be897e93Chris Banes protected void onDetachedFromWindow() { 445d9770e12c8ff2d4417700492c6616572be897e93Chris Banes super.onDetachedFromWindow(); 446d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().onDetachedFromWindow(); 447d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 448d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 449d9770e12c8ff2d4417700492c6616572be897e93Chris Banes @Override 4509840efe3dbdc7026521da8576574c55120782f6cChris Banes protected void drawableStateChanged() { 4519840efe3dbdc7026521da8576574c55120782f6cChris Banes super.drawableStateChanged(); 452d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().onDrawableStateChanged(getDrawableState()); 4539840efe3dbdc7026521da8576574c55120782f6cChris Banes } 4549840efe3dbdc7026521da8576574c55120782f6cChris Banes 4559840efe3dbdc7026521da8576574c55120782f6cChris Banes @TargetApi(Build.VERSION_CODES.HONEYCOMB) 4569840efe3dbdc7026521da8576574c55120782f6cChris Banes @Override 4579840efe3dbdc7026521da8576574c55120782f6cChris Banes public void jumpDrawablesToCurrentState() { 4589840efe3dbdc7026521da8576574c55120782f6cChris Banes super.jumpDrawablesToCurrentState(); 459d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().jumpDrawableToCurrentState(); 4609840efe3dbdc7026521da8576574c55120782f6cChris Banes } 4619840efe3dbdc7026521da8576574c55120782f6cChris Banes 462d9cbe69a6661315238d856abc22578d03666f63bChris Banes /** 463d9cbe69a6661315238d856abc22578d03666f63bChris Banes * Return in {@code rect} the bounds of the actual floating action button content in view-local 464d9cbe69a6661315238d856abc22578d03666f63bChris Banes * coordinates. This is defined as anything within any visible shadow. 465d9cbe69a6661315238d856abc22578d03666f63bChris Banes * 466d9cbe69a6661315238d856abc22578d03666f63bChris Banes * @return true if this view actually has been laid out and has a content rect, else false. 467d9cbe69a6661315238d856abc22578d03666f63bChris Banes */ 468d9cbe69a6661315238d856abc22578d03666f63bChris Banes public boolean getContentRect(@NonNull Rect rect) { 469d9cbe69a6661315238d856abc22578d03666f63bChris Banes if (ViewCompat.isLaidOut(this)) { 470d9cbe69a6661315238d856abc22578d03666f63bChris Banes rect.set(0, 0, getWidth(), getHeight()); 471d9cbe69a6661315238d856abc22578d03666f63bChris Banes rect.left += mShadowPadding.left; 472d9cbe69a6661315238d856abc22578d03666f63bChris Banes rect.top += mShadowPadding.top; 473d9cbe69a6661315238d856abc22578d03666f63bChris Banes rect.right -= mShadowPadding.right; 474d9cbe69a6661315238d856abc22578d03666f63bChris Banes rect.bottom -= mShadowPadding.bottom; 475d9cbe69a6661315238d856abc22578d03666f63bChris Banes return true; 476d9cbe69a6661315238d856abc22578d03666f63bChris Banes } else { 477d9cbe69a6661315238d856abc22578d03666f63bChris Banes return false; 478d9cbe69a6661315238d856abc22578d03666f63bChris Banes } 479d9cbe69a6661315238d856abc22578d03666f63bChris Banes } 480d9cbe69a6661315238d856abc22578d03666f63bChris Banes 4813d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes /** 4823d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes * Returns the FloatingActionButton's background, minus any compatible shadow implementation. 4833d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes */ 4843d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes @NonNull 4853d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes public Drawable getContentBackground() { 486d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return getImpl().getContentBackground(); 4873d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 4883d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 4899840efe3dbdc7026521da8576574c55120782f6cChris Banes private static int resolveAdjustedSize(int desiredSize, int measureSpec) { 4909840efe3dbdc7026521da8576574c55120782f6cChris Banes int result = desiredSize; 4919840efe3dbdc7026521da8576574c55120782f6cChris Banes int specMode = MeasureSpec.getMode(measureSpec); 4929840efe3dbdc7026521da8576574c55120782f6cChris Banes int specSize = MeasureSpec.getSize(measureSpec); 4939840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (specMode) { 4949840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.UNSPECIFIED: 4959840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want. Just don't be larger 4969840efe3dbdc7026521da8576574c55120782f6cChris Banes // than max size imposed on ourselves. 4979840efe3dbdc7026521da8576574c55120782f6cChris Banes result = desiredSize; 4989840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 4999840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.AT_MOST: 5009840efe3dbdc7026521da8576574c55120782f6cChris Banes // Parent says we can be as big as we want, up to specSize. 5019840efe3dbdc7026521da8576574c55120782f6cChris Banes // Don't be larger than specSize, and don't be larger than 5029840efe3dbdc7026521da8576574c55120782f6cChris Banes // the max size imposed on ourselves. 5039840efe3dbdc7026521da8576574c55120782f6cChris Banes result = Math.min(desiredSize, specSize); 5049840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 5059840efe3dbdc7026521da8576574c55120782f6cChris Banes case MeasureSpec.EXACTLY: 5069840efe3dbdc7026521da8576574c55120782f6cChris Banes // No choice. Do what we are told. 5079840efe3dbdc7026521da8576574c55120782f6cChris Banes result = specSize; 5089840efe3dbdc7026521da8576574c55120782f6cChris Banes break; 5099840efe3dbdc7026521da8576574c55120782f6cChris Banes } 5109840efe3dbdc7026521da8576574c55120782f6cChris Banes return result; 5119840efe3dbdc7026521da8576574c55120782f6cChris Banes } 5129840efe3dbdc7026521da8576574c55120782f6cChris Banes 5139840efe3dbdc7026521da8576574c55120782f6cChris Banes static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { 5149840efe3dbdc7026521da8576574c55120782f6cChris Banes switch (value) { 5159840efe3dbdc7026521da8576574c55120782f6cChris Banes case 3: 5169840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_OVER; 5179840efe3dbdc7026521da8576574c55120782f6cChris Banes case 5: 5189840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_IN; 5199840efe3dbdc7026521da8576574c55120782f6cChris Banes case 9: 5209840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SRC_ATOP; 5219840efe3dbdc7026521da8576574c55120782f6cChris Banes case 14: 5229840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.MULTIPLY; 5239840efe3dbdc7026521da8576574c55120782f6cChris Banes case 15: 5249840efe3dbdc7026521da8576574c55120782f6cChris Banes return PorterDuff.Mode.SCREEN; 5259840efe3dbdc7026521da8576574c55120782f6cChris Banes default: 5269840efe3dbdc7026521da8576574c55120782f6cChris Banes return defaultMode; 5279840efe3dbdc7026521da8576574c55120782f6cChris Banes } 5289840efe3dbdc7026521da8576574c55120782f6cChris Banes } 52914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes 530d5d3213d80be767482a89bea6d073006582e2bccChris Banes @Override 531d5d3213d80be767482a89bea6d073006582e2bccChris Banes public boolean onTouchEvent(MotionEvent ev) { 532d5d3213d80be767482a89bea6d073006582e2bccChris Banes if(getContentRect(mTouchArea) && !mTouchArea.contains((int) ev.getX(), (int) ev.getY())) { 533d5d3213d80be767482a89bea6d073006582e2bccChris Banes return false; 534d5d3213d80be767482a89bea6d073006582e2bccChris Banes } 535d5d3213d80be767482a89bea6d073006582e2bccChris Banes 536d5d3213d80be767482a89bea6d073006582e2bccChris Banes return super.onTouchEvent(ev); 537d5d3213d80be767482a89bea6d073006582e2bccChris Banes } 538d5d3213d80be767482a89bea6d073006582e2bccChris Banes 53914d064edb3e4a16a3b90a4a850560177bea1e60dChris Banes /** 5401005c226fdfddf30e6ad1ec2bb4c57942918dd8fKirill Grouchnikov * Behavior designed for use with {@link FloatingActionButton} instances. Its main function 541b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * is to move {@link FloatingActionButton} views so that any displayed {@link Snackbar}s do 542b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * not cover them. 543b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 544b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> { 545b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is 546b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // because we can use view translation properties which greatly simplifies the code. 547b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; 548b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 549a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes private ValueAnimatorCompat mFabTranslationYAnimator; 55018d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes private float mFabTranslationY; 551a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private Rect mTmpRect; 552b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 553ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes public Behavior() { 554ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes super(); 555ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes } 556ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes 557ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes public Behavior(Context context, AttributeSet attrs) { 558ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes super(context, attrs); 559ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes } 560ea0bce9137a7d61b710f8fe299e4790e8f7f1540Chris Banes 561b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 562b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean layoutDependsOn(CoordinatorLayout parent, 563be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes FloatingActionButton child, View dependency) { 564a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes // We're dependent on all SnackbarLayouts (if enabled) 565a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; 566b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 567b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 568b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 569b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, 570a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes View dependency) { 571a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (dependency instanceof Snackbar.SnackbarLayout) { 572ae0f778788b739d75f704dd4726c22017e30799eChris Banes updateFabTranslationForSnackbar(parent, child, true); 573a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } else if (dependency instanceof AppBarLayout) { 574be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // If we're depending on an AppBarLayout we will show/hide it automatically 575be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // if the FAB is anchored to the AppBarLayout 576be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes updateFabVisibility(parent, (AppBarLayout) dependency, child); 577be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 578be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return false; 579be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 580a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 581c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes @Override 582c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes public void onDependentViewRemoved(CoordinatorLayout parent, FloatingActionButton child, 583c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes View dependency) { 584c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes if (dependency instanceof Snackbar.SnackbarLayout) { 5859cf7823a2b044e550dd5c1ba3a7a91d45b4d9673Chris Banes updateFabTranslationForSnackbar(parent, child, true); 586c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes } 587c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes } 588c036550c2328e8489d79c79e88f5de7f88449be6Chris Banes 589be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes private boolean updateFabVisibility(CoordinatorLayout parent, 590be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes AppBarLayout appBarLayout, FloatingActionButton child) { 591be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final CoordinatorLayout.LayoutParams lp = 592be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes (CoordinatorLayout.LayoutParams) child.getLayoutParams(); 593be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (lp.getAnchorId() != appBarLayout.getId()) { 594be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // The anchor ID doesn't match the dependency, so we won't automatically 595be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // show/hide the FAB 596be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return false; 597be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 598a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 599fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes if (child.getUserSetVisibility() != VISIBLE) { 6001005c226fdfddf30e6ad1ec2bb4c57942918dd8fKirill Grouchnikov // The view isn't set to be visible so skip changing its visibility 601fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes return false; 602fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes } 603fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes 604be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (mTmpRect == null) { 605be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes mTmpRect = new Rect(); 606a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 607be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 608be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // First, let's get the visible rect of the dependency 609be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final Rect rect = mTmpRect; 610be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect); 611be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 612be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) { 613be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // If the anchor's bottom is below the seam, we'll animate our FAB out 614fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes child.hide(null, false); 615be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } else { 616be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // Else, we'll animate our FAB back in 617fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes child.show(null, false); 618be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 619be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes return true; 620b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 621b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 622a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private void updateFabTranslationForSnackbar(CoordinatorLayout parent, 623ae0f778788b739d75f704dd4726c22017e30799eChris Banes final FloatingActionButton fab, boolean animationAllowed) { 62418d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes final float targetTransY = getFabTranslationYForSnackbar(parent, fab); 62518d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes if (mFabTranslationY == targetTransY) { 62618d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes // We're already at (or currently animating to) the target value, return... 62718d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes return; 62818d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes } 62918d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes 63018d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes final float currentTransY = ViewCompat.getTranslationY(fab); 63118d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes 632a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes // Make sure that any current animation is cancelled 633a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes if (mFabTranslationYAnimator != null && mFabTranslationYAnimator.isRunning()) { 634a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator.cancel(); 635a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes } 636a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes 637ae0f778788b739d75f704dd4726c22017e30799eChris Banes if (animationAllowed && fab.isShown() 638e33473d0e35315c02243363a7479a2c361765751Chris Banes && Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) { 6391005c226fdfddf30e6ad1ec2bb4c57942918dd8fKirill Grouchnikov // If the FAB will be travelling by more than 2/3 of its height, let's animate 64018d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes // it instead 641a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes if (mFabTranslationYAnimator == null) { 642a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator = ViewUtils.createAnimator(); 643a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator.setInterpolator( 644a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); 645a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator.setUpdateListener( 646a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes new ValueAnimatorCompat.AnimatorUpdateListener() { 647a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes @Override 648a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes public void onAnimationUpdate(ValueAnimatorCompat animator) { 649a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes ViewCompat.setTranslationY(fab, 650a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes animator.getAnimatedFloatValue()); 651a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes } 652a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes }); 653a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes } 654a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator.setFloatValues(currentTransY, targetTransY); 655a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationYAnimator.start(); 65618d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes } else { 65718d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes // Now update the translation Y 65818d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes ViewCompat.setTranslationY(fab, targetTransY); 65918d22257ccfb5cebb3ccd2450736e735ed1fb9bbChris Banes } 660a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes 661a419ee1ef9aef8b567f1ccd8c29d01ec7bff4cc9Chris Banes mFabTranslationY = targetTransY; 662b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 663b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 664a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private float getFabTranslationYForSnackbar(CoordinatorLayout parent, 665a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes FloatingActionButton fab) { 666b7f9224b1495db47eb8fd813b5912250e900770aChris Banes float minOffset = 0; 667a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final List<View> dependencies = parent.getDependencies(fab); 668a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes for (int i = 0, z = dependencies.size(); i < z; i++) { 669a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final View view = dependencies.get(i); 670a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) { 671a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes minOffset = Math.min(minOffset, 672a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes ViewCompat.getTranslationY(view) - view.getHeight()); 673b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 674b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 675a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 676b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return minOffset; 677b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 678b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 679e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes @Override 680e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child, 681e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes int layoutDirection) { 6821005c226fdfddf30e6ad1ec2bb4c57942918dd8fKirill Grouchnikov // First, let's make sure that the visibility of the FAB is consistent 683be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final List<View> dependencies = parent.getDependencies(child); 684be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes for (int i = 0, count = dependencies.size(); i < count; i++) { 685be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes final View dependency = dependencies.get(i); 686be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes if (dependency instanceof AppBarLayout 687be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes && updateFabVisibility(parent, (AppBarLayout) dependency, child)) { 688be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes break; 689be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 690be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes } 691be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes // Now let the CoordinatorLayout lay out the FAB 692e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes parent.onLayoutChild(child, layoutDirection); 693e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // Now offset it if needed 694e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetIfNeeded(parent, child); 695ae0f778788b739d75f704dd4726c22017e30799eChris Banes // Make sure we translate the FAB for any displayed Snackbars (without an animation) 696ae0f778788b739d75f704dd4726c22017e30799eChris Banes updateFabTranslationForSnackbar(parent, child, false); 697e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes return true; 698e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 699e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 700e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes /** 701e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method 702e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * offsets our layout position so that we're positioned correctly if we're on one of 703e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes * our parent's edges. 704e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes */ 705e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes private void offsetIfNeeded(CoordinatorLayout parent, FloatingActionButton fab) { 706e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes final Rect padding = fab.mShadowPadding; 707e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 708e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (padding != null && padding.centerX() > 0 && padding.centerY() > 0) { 709e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes final CoordinatorLayout.LayoutParams lp = 710e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); 711e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 712e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes int offsetTB = 0, offsetLR = 0; 713e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 714e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (fab.getRight() >= parent.getWidth() - lp.rightMargin) { 715e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the left edge, shift it the right 716e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetLR = padding.right; 717e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } else if (fab.getLeft() <= lp.leftMargin) { 718e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the left edge, shift it the left 719e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetLR = -padding.left; 720e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 721e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes if (fab.getBottom() >= parent.getBottom() - lp.bottomMargin) { 722e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the bottom edge, shift it down 723e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetTB = padding.bottom; 724e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } else if (fab.getTop() <= lp.topMargin) { 725e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes // If we're on the top edge, shift it up 726e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes offsetTB = -padding.top; 727e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 728e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes 729e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes fab.offsetTopAndBottom(offsetTB); 730e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes fab.offsetLeftAndRight(offsetLR); 731e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 732e882ef3492de3d2bb687b454e08b870b06d8f4e2Chris Banes } 733b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 7346d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 7356d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes /** 7366d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * Returns the backward compatible elevation of the FloatingActionButton. 7376d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 7382b1d1d93a93070601d3894f523d6421c64544246Chris Banes * @return the backward compatible elevation in pixels. 7396d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation 7401e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes * @see #setCompatElevation(float) 7416d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes */ 7421e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes public float getCompatElevation() { 743d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return getImpl().getElevation(); 7446d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 7456d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 7466d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes /** 7476d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * Updates the backward compatible elevation of the FloatingActionButton. 7486d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * 7496d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @param elevation The backward compatible elevation in pixels. 7506d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @attr ref android.support.design.R.styleable#FloatingActionButton_elevation 7511e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes * @see #getCompatElevation() 7526d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes * @see #setUseCompatPadding(boolean) 7536d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes */ 7541e220ff878c4b9c22aff3a6afc20aa89449c1833Chris Banes public void setCompatElevation(float elevation) { 755d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes getImpl().setElevation(elevation); 7566d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 757cdc736866534c604c4015c78371ade52bb6d52dfChris Banes 758d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes private FloatingActionButtonImpl getImpl() { 759d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes if (mImpl == null) { 760d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes mImpl = createImpl(); 761d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes } 762d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return mImpl; 763d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes } 764d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes 765d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes private FloatingActionButtonImpl createImpl() { 766cdc736866534c604c4015c78371ade52bb6d52dfChris Banes final int sdk = Build.VERSION.SDK_INT; 767cdc736866534c604c4015c78371ade52bb6d52dfChris Banes if (sdk >= 21) { 768d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl()); 769cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } else if (sdk >= 14) { 770d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return new FloatingActionButtonIcs(this, new ShadowDelegateImpl()); 771cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } else { 772d95e0bb0271ae59387dd9d2ca402ad6f39d789d2Chris Banes return new FloatingActionButtonEclairMr1(this, new ShadowDelegateImpl()); 773cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 774cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 775cdc736866534c604c4015c78371ade52bb6d52dfChris Banes 776cdc736866534c604c4015c78371ade52bb6d52dfChris Banes private class ShadowDelegateImpl implements ShadowViewDelegate { 777cdc736866534c604c4015c78371ade52bb6d52dfChris Banes @Override 778cdc736866534c604c4015c78371ade52bb6d52dfChris Banes public float getRadius() { 779cdc736866534c604c4015c78371ade52bb6d52dfChris Banes return getSizeDimension() / 2f; 780cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 781cdc736866534c604c4015c78371ade52bb6d52dfChris Banes 782cdc736866534c604c4015c78371ade52bb6d52dfChris Banes @Override 783cdc736866534c604c4015c78371ade52bb6d52dfChris Banes public void setShadowPadding(int left, int top, int right, int bottom) { 784cdc736866534c604c4015c78371ade52bb6d52dfChris Banes mShadowPadding.set(left, top, right, bottom); 785cdc736866534c604c4015c78371ade52bb6d52dfChris Banes setPadding(left + mImagePadding, top + mImagePadding, 786cdc736866534c604c4015c78371ade52bb6d52dfChris Banes right + mImagePadding, bottom + mImagePadding); 787cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 788cdc736866534c604c4015c78371ade52bb6d52dfChris Banes 789cdc736866534c604c4015c78371ade52bb6d52dfChris Banes @Override 790cdc736866534c604c4015c78371ade52bb6d52dfChris Banes public void setBackgroundDrawable(Drawable background) { 791cdc736866534c604c4015c78371ade52bb6d52dfChris Banes FloatingActionButton.super.setBackgroundDrawable(background); 792cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 793cdc736866534c604c4015c78371ade52bb6d52dfChris Banes 794cdc736866534c604c4015c78371ade52bb6d52dfChris Banes @Override 795cdc736866534c604c4015c78371ade52bb6d52dfChris Banes public boolean isCompatPaddingEnabled() { 796cdc736866534c604c4015c78371ade52bb6d52dfChris Banes return mCompatPadding; 797cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 798cdc736866534c604c4015c78371ade52bb6d52dfChris Banes } 7999840efe3dbdc7026521da8576574c55120782f6cChris Banes} 800