FloatingActionButtonImpl.java revision 15d1695da89eed196884b44fe90f19f83fb37a4f
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.design.widget;
18
19import android.content.res.ColorStateList;
20import android.content.res.Resources;
21import android.graphics.Color;
22import android.graphics.PorterDuff;
23import android.graphics.Rect;
24import android.graphics.drawable.Drawable;
25import android.graphics.drawable.GradientDrawable;
26import android.support.annotation.Nullable;
27import android.support.design.R;
28import android.view.View;
29import android.view.ViewTreeObserver;
30import android.view.animation.Interpolator;
31
32abstract class FloatingActionButtonImpl {
33
34    static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR;
35    static final long PRESSED_ANIM_DURATION = 100;
36    static final long PRESSED_ANIM_DELAY = 100;
37
38    static final int ANIM_STATE_NONE = 0;
39    static final int ANIM_STATE_HIDING = 1;
40    static final int ANIM_STATE_SHOWING = 2;
41
42    int mAnimState = ANIM_STATE_NONE;
43
44    Drawable mShapeDrawable;
45    Drawable mRippleDrawable;
46    CircularBorderDrawable mBorderDrawable;
47    Drawable mContentBackground;
48
49    float mElevation;
50    float mPressedTranslationZ;
51
52    interface InternalVisibilityChangedListener {
53        public void onShown();
54        public void onHidden();
55    }
56
57    static final int SHOW_HIDE_ANIM_DURATION = 200;
58
59    static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
60            android.R.attr.state_enabled};
61    static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
62            android.R.attr.state_enabled};
63    static final int[] ENABLED_STATE_SET = {android.R.attr.state_enabled};
64    static final int[] EMPTY_STATE_SET = new int[0];
65
66    final VisibilityAwareImageButton mView;
67    final ShadowViewDelegate mShadowViewDelegate;
68    final ValueAnimatorCompat.Creator mAnimatorCreator;
69
70    private final Rect mTmpRect = new Rect();
71    private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
72
73    FloatingActionButtonImpl(VisibilityAwareImageButton view,
74            ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
75        mView = view;
76        mShadowViewDelegate = shadowViewDelegate;
77        mAnimatorCreator = animatorCreator;
78    }
79
80    abstract void setBackgroundDrawable(ColorStateList backgroundTint,
81            PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth);
82
83    abstract void setBackgroundTintList(ColorStateList tint);
84
85    abstract void setBackgroundTintMode(PorterDuff.Mode tintMode);
86
87    abstract void setRippleColor(int rippleColor);
88
89    final void setElevation(float elevation) {
90        if (mElevation != elevation) {
91            mElevation = elevation;
92            onElevationsChanged(elevation, mPressedTranslationZ);
93        }
94    }
95
96    abstract float getElevation();
97
98    final void setPressedTranslationZ(float translationZ) {
99        if (mPressedTranslationZ != translationZ) {
100            mPressedTranslationZ = translationZ;
101            onElevationsChanged(mElevation, translationZ);
102        }
103    }
104
105    abstract void onElevationsChanged(float elevation, float pressedTranslationZ);
106
107    abstract void onDrawableStateChanged(int[] state);
108
109    abstract void jumpDrawableToCurrentState();
110
111    abstract void hide(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
112
113    abstract void show(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
114
115    final Drawable getContentBackground() {
116        return mContentBackground;
117    }
118
119    abstract void onCompatShadowChanged();
120
121    final void updatePadding() {
122        Rect rect = mTmpRect;
123        getPadding(rect);
124        onPaddingUpdated(rect);
125        mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
126    }
127
128    abstract void getPadding(Rect rect);
129
130    void onPaddingUpdated(Rect padding) {}
131
132    void onAttachedToWindow() {
133        if (requirePreDrawListener()) {
134            ensurePreDrawListener();
135            mView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
136        }
137    }
138
139    void onDetachedFromWindow() {
140        if (mPreDrawListener != null) {
141            mView.getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
142            mPreDrawListener = null;
143        }
144    }
145
146    boolean requirePreDrawListener() {
147        return false;
148    }
149
150    CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
151        final Resources resources = mView.getResources();
152        CircularBorderDrawable borderDrawable = newCircularDrawable();
153        borderDrawable.setGradientColors(
154                resources.getColor(R.color.design_fab_stroke_top_outer_color),
155                resources.getColor(R.color.design_fab_stroke_top_inner_color),
156                resources.getColor(R.color.design_fab_stroke_end_inner_color),
157                resources.getColor(R.color.design_fab_stroke_end_outer_color));
158        borderDrawable.setBorderWidth(borderWidth);
159        borderDrawable.setBorderTint(backgroundTint);
160        return borderDrawable;
161    }
162
163    CircularBorderDrawable newCircularDrawable() {
164        return new CircularBorderDrawable();
165    }
166
167    void onPreDraw() {
168    }
169
170    private void ensurePreDrawListener() {
171        if (mPreDrawListener == null) {
172            mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
173                @Override
174                public boolean onPreDraw() {
175                    FloatingActionButtonImpl.this.onPreDraw();
176                    return true;
177                }
178            };
179        }
180    }
181
182    GradientDrawable createShapeDrawable() {
183        GradientDrawable d = new GradientDrawable();
184        d.setShape(GradientDrawable.OVAL);
185        d.setColor(Color.WHITE);
186        return d;
187    }
188
189    boolean isOrWillBeShown() {
190        if (mView.getVisibility() != View.VISIBLE) {
191            // If we not currently visible, return true if we're animating to be shown
192            return mAnimState == ANIM_STATE_SHOWING;
193        } else {
194            // Otherwise if we're visible, return true if we're not animating to be hidden
195            return mAnimState != ANIM_STATE_HIDING;
196        }
197    }
198
199    boolean isOrWillBeHidden() {
200        if (mView.getVisibility() == View.VISIBLE) {
201            // If we currently visible, return true if we're animating to be hidden
202            return mAnimState == ANIM_STATE_HIDING;
203        } else {
204            // Otherwise if we're not visible, return true if we're not animating to be shown
205            return mAnimState != ANIM_STATE_SHOWING;
206        }
207    }
208}
209