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 197b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikasimport android.content.Context; 209840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.content.res.ColorStateList; 2180de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banesimport android.content.res.Resources; 22097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.graphics.Color; 239840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.PorterDuff; 246d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banesimport android.graphics.Rect; 259840efe3dbdc7026521da8576574c55120782f6cChris Banesimport android.graphics.drawable.Drawable; 26097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banesimport android.graphics.drawable.GradientDrawable; 270ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Weiimport android.support.annotation.Nullable; 28cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banesimport android.support.design.R; 297b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikasimport android.support.v4.content.ContextCompat; 3015d1695da89eed196884b44fe90f19f83fb37a4fChris Banesimport android.view.View; 31d9770e12c8ff2d4417700492c6616572be897e93Chris Banesimport android.view.ViewTreeObserver; 32f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banesimport android.view.animation.Interpolator; 339840efe3dbdc7026521da8576574c55120782f6cChris Banes 349840efe3dbdc7026521da8576574c55120782f6cChris Banesabstract class FloatingActionButtonImpl { 359840efe3dbdc7026521da8576574c55120782f6cChris Banes 36f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR; 37f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes static final long PRESSED_ANIM_DURATION = 100; 38f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes static final long PRESSED_ANIM_DELAY = 100; 39f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes 4015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes static final int ANIM_STATE_NONE = 0; 4115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes static final int ANIM_STATE_HIDING = 1; 4215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes static final int ANIM_STATE_SHOWING = 2; 4315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes 4415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes int mAnimState = ANIM_STATE_NONE; 4515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes 463d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes Drawable mShapeDrawable; 473d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes Drawable mRippleDrawable; 483d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes CircularBorderDrawable mBorderDrawable; 493d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes Drawable mContentBackground; 503d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 513d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes float mElevation; 523d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes float mPressedTranslationZ; 533d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 540ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei interface InternalVisibilityChangedListener { 550ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onShown(); 560ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei public void onHidden(); 570ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei } 580ad7ef59b28d8ffafd551d2756b5a8ec47c90682Mark Wei 59be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes static final int SHOW_HIDE_ANIM_DURATION = 200; 60be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 619840efe3dbdc7026521da8576574c55120782f6cChris Banes static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed, 629840efe3dbdc7026521da8576574c55120782f6cChris Banes android.R.attr.state_enabled}; 639840efe3dbdc7026521da8576574c55120782f6cChris Banes static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused, 649840efe3dbdc7026521da8576574c55120782f6cChris Banes android.R.attr.state_enabled}; 65f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes static final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled}; 669840efe3dbdc7026521da8576574c55120782f6cChris Banes static final int[] EMPTY_STATE_SET = new int[0]; 679840efe3dbdc7026521da8576574c55120782f6cChris Banes 68fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes final VisibilityAwareImageButton mView; 699840efe3dbdc7026521da8576574c55120782f6cChris Banes final ShadowViewDelegate mShadowViewDelegate; 705a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes final ValueAnimatorCompat.Creator mAnimatorCreator; 719840efe3dbdc7026521da8576574c55120782f6cChris Banes 726d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes private final Rect mTmpRect = new Rect(); 73d9770e12c8ff2d4417700492c6616572be897e93Chris Banes private ViewTreeObserver.OnPreDrawListener mPreDrawListener; 74d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 75fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes FloatingActionButtonImpl(VisibilityAwareImageButton view, 765a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) { 779840efe3dbdc7026521da8576574c55120782f6cChris Banes mView = view; 789840efe3dbdc7026521da8576574c55120782f6cChris Banes mShadowViewDelegate = shadowViewDelegate; 795a0083cc8328f05b15efe0293b7aa83723cdd9adChris Banes mAnimatorCreator = animatorCreator; 809840efe3dbdc7026521da8576574c55120782f6cChris Banes } 819840efe3dbdc7026521da8576574c55120782f6cChris Banes 82097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes abstract void setBackgroundDrawable(ColorStateList backgroundTint, 83cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth); 849840efe3dbdc7026521da8576574c55120782f6cChris Banes 859840efe3dbdc7026521da8576574c55120782f6cChris Banes abstract void setBackgroundTintList(ColorStateList tint); 869840efe3dbdc7026521da8576574c55120782f6cChris Banes 879840efe3dbdc7026521da8576574c55120782f6cChris Banes abstract void setBackgroundTintMode(PorterDuff.Mode tintMode); 889840efe3dbdc7026521da8576574c55120782f6cChris Banes 899840efe3dbdc7026521da8576574c55120782f6cChris Banes abstract void setRippleColor(int rippleColor); 909840efe3dbdc7026521da8576574c55120782f6cChris Banes 913d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes final void setElevation(float elevation) { 923d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes if (mElevation != elevation) { 933d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes mElevation = elevation; 94f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes onElevationsChanged(elevation, mPressedTranslationZ); 953d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 963d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 973d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 986d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes abstract float getElevation(); 996d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 1003d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes final void setPressedTranslationZ(float translationZ) { 1013d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes if (mPressedTranslationZ != translationZ) { 1023d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes mPressedTranslationZ = translationZ; 103f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes onElevationsChanged(mElevation, translationZ); 1043d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 1053d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 1063d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 107f0aec033caa46d2cdddad020b8748d24f921ad5bChris Banes abstract void onElevationsChanged(float elevation, float pressedTranslationZ); 1089840efe3dbdc7026521da8576574c55120782f6cChris Banes 1099840efe3dbdc7026521da8576574c55120782f6cChris Banes abstract void onDrawableStateChanged(int[] state); 1109840efe3dbdc7026521da8576574c55120782f6cChris Banes 1119840efe3dbdc7026521da8576574c55120782f6cChris Banes abstract void jumpDrawableToCurrentState(); 1129840efe3dbdc7026521da8576574c55120782f6cChris Banes 113fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes abstract void hide(@Nullable InternalVisibilityChangedListener listener, boolean fromUser); 114be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 115fc780bab91bd4275ae2c3b75c3dfb327e008e4dbChris Banes abstract void show(@Nullable InternalVisibilityChangedListener listener, boolean fromUser); 116be48ed9161c09c4b2178ab6dbe28638222809fc7Chris Banes 1173d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes final Drawable getContentBackground() { 1183d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes return mContentBackground; 1193d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes } 1203d81c900316412b4130bf40e0dd8b0d3d3a93e78Chris Banes 1216d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes abstract void onCompatShadowChanged(); 1226d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 1236d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes final void updatePadding() { 1246d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes Rect rect = mTmpRect; 1256d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes getPadding(rect); 1266d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes onPaddingUpdated(rect); 1276d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom); 1286d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes } 1296d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 1306d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes abstract void getPadding(Rect rect); 1316d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 1326d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes void onPaddingUpdated(Rect padding) {} 1336d7a9a02765e4cb497081e66dafb5d9fa76f4312Chris Banes 134d9770e12c8ff2d4417700492c6616572be897e93Chris Banes void onAttachedToWindow() { 135d9770e12c8ff2d4417700492c6616572be897e93Chris Banes if (requirePreDrawListener()) { 136d9770e12c8ff2d4417700492c6616572be897e93Chris Banes ensurePreDrawListener(); 137d9770e12c8ff2d4417700492c6616572be897e93Chris Banes mView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); 138d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 139d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 140d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 141d9770e12c8ff2d4417700492c6616572be897e93Chris Banes void onDetachedFromWindow() { 142d9770e12c8ff2d4417700492c6616572be897e93Chris Banes if (mPreDrawListener != null) { 143d9770e12c8ff2d4417700492c6616572be897e93Chris Banes mView.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener); 144d9770e12c8ff2d4417700492c6616572be897e93Chris Banes mPreDrawListener = null; 145d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 146d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 147d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 148d9770e12c8ff2d4417700492c6616572be897e93Chris Banes boolean requirePreDrawListener() { 149d9770e12c8ff2d4417700492c6616572be897e93Chris Banes return false; 150d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 151d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 152d9770e12c8ff2d4417700492c6616572be897e93Chris Banes CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) { 1537b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas final Context context = mView.getContext(); 15480de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes CircularBorderDrawable borderDrawable = newCircularDrawable(); 15580de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes borderDrawable.setGradientColors( 1567b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas ContextCompat.getColor(context, R.color.design_fab_stroke_top_outer_color), 1577b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas ContextCompat.getColor(context, R.color.design_fab_stroke_top_inner_color), 1587b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas ContextCompat.getColor(context, R.color.design_fab_stroke_end_inner_color), 1597b75d53b8e5584895595f801256a3d63bb68148aAurimas Liutikas ContextCompat.getColor(context, R.color.design_fab_stroke_end_outer_color)); 16080de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes borderDrawable.setBorderWidth(borderWidth); 1618603357c9433b9aef1805413a3bfc4a4f8decc50Chris Banes borderDrawable.setBorderTint(backgroundTint); 162092f4b4fc5610ed1a2c4f5108066726ee59d8f16Chris Banes return borderDrawable; 16380de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes } 16480de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes 16580de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes CircularBorderDrawable newCircularDrawable() { 16680de0674c28a2bd9ade11f24a3b0e46ea83b6847Chris Banes return new CircularBorderDrawable(); 167cd78954a2b32d9c22686f12c194fac7e49566cf6Chris Banes } 168d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 169d9770e12c8ff2d4417700492c6616572be897e93Chris Banes void onPreDraw() { 170d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 171d9770e12c8ff2d4417700492c6616572be897e93Chris Banes 172d9770e12c8ff2d4417700492c6616572be897e93Chris Banes private void ensurePreDrawListener() { 173d9770e12c8ff2d4417700492c6616572be897e93Chris Banes if (mPreDrawListener == null) { 174d9770e12c8ff2d4417700492c6616572be897e93Chris Banes mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { 175d9770e12c8ff2d4417700492c6616572be897e93Chris Banes @Override 176d9770e12c8ff2d4417700492c6616572be897e93Chris Banes public boolean onPreDraw() { 177d9770e12c8ff2d4417700492c6616572be897e93Chris Banes FloatingActionButtonImpl.this.onPreDraw(); 178d9770e12c8ff2d4417700492c6616572be897e93Chris Banes return true; 179d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 180d9770e12c8ff2d4417700492c6616572be897e93Chris Banes }; 181d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 182d9770e12c8ff2d4417700492c6616572be897e93Chris Banes } 183097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes 184097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes GradientDrawable createShapeDrawable() { 185097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes GradientDrawable d = new GradientDrawable(); 186097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes d.setShape(GradientDrawable.OVAL); 187097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes d.setColor(Color.WHITE); 188097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes return d; 189097e80a3c5518c6bf2e9f3f9b55ed9f4b5cc37e8Chris Banes } 19015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes 19115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes boolean isOrWillBeShown() { 19215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes if (mView.getVisibility() != View.VISIBLE) { 19315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes // If we not currently visible, return true if we're animating to be shown 19415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes return mAnimState == ANIM_STATE_SHOWING; 19515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } else { 19615d1695da89eed196884b44fe90f19f83fb37a4fChris Banes // Otherwise if we're visible, return true if we're not animating to be hidden 19715d1695da89eed196884b44fe90f19f83fb37a4fChris Banes return mAnimState != ANIM_STATE_HIDING; 19815d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } 19915d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } 20015d1695da89eed196884b44fe90f19f83fb37a4fChris Banes 20115d1695da89eed196884b44fe90f19f83fb37a4fChris Banes boolean isOrWillBeHidden() { 20215d1695da89eed196884b44fe90f19f83fb37a4fChris Banes if (mView.getVisibility() == View.VISIBLE) { 20315d1695da89eed196884b44fe90f19f83fb37a4fChris Banes // If we currently visible, return true if we're animating to be hidden 20415d1695da89eed196884b44fe90f19f83fb37a4fChris Banes return mAnimState == ANIM_STATE_HIDING; 20515d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } else { 20615d1695da89eed196884b44fe90f19f83fb37a4fChris Banes // Otherwise if we're not visible, return true if we're not animating to be shown 20715d1695da89eed196884b44fe90f19f83fb37a4fChris Banes return mAnimState != ANIM_STATE_SHOWING; 20815d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } 20915d1695da89eed196884b44fe90f19f83fb37a4fChris Banes } 2109840efe3dbdc7026521da8576574c55120782f6cChris Banes} 211