OSDN Git Service

Removing custom state definition from FastBitmapDrawable
authorSunny Goyal <sunnygoyal@google.com>
Thu, 16 Feb 2017 21:33:15 +0000 (13:33 -0800)
committerSunny Goyal <sunnygoyal@google.com>
Fri, 24 Feb 2017 00:15:25 +0000 (16:15 -0800)
> For Fast scrolling, using state list animator defined in xml
> For Pressed state, using onState change method in a drawable

Change-Id: Ia608690f593938cf0f77e00afcc2a3076e48d8f4

res/animator/all_apps_fastscroll_icon_anim.xml [new file with mode: 0644]
res/animator/overview_button_anim.xml [moved from res/animator-v21/overview_button_anim.xml with 100% similarity]
res/layout/all_apps_icon.xml
src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
src/com/android/launcher3/BubbleTextView.java
src/com/android/launcher3/FastBitmapDrawable.java
src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java

diff --git a/res/animator/all_apps_fastscroll_icon_anim.xml b/res/animator/all_apps_fastscroll_icon_anim.xml
new file mode 100644 (file)
index 0000000..380b009
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_activated="true">
+        <set>
+            <objectAnimator
+                android:duration="225"
+                android:propertyName="scaleX"
+                android:valueTo="1.15"
+                android:valueType="floatType" />
+            <objectAnimator
+                android:duration="225"
+                android:propertyName="scaleY"
+                android:valueTo="1.15"
+                android:valueType="floatType" />
+        </set>
+    </item>
+
+    <item>
+        <set>
+            <objectAnimator
+                android:duration="275"
+                android:propertyName="scaleX"
+                android:valueTo="1"
+                android:valueType="floatType" />
+            <objectAnimator
+                android:duration="275"
+                android:propertyName="scaleY"
+                android:valueTo="1"
+                android:valueType="floatType" />
+        </set>
+    </item>
+
+</selector>
\ No newline at end of file
index 1e83041..ca0cbcc 100644 (file)
@@ -20,6 +20,7 @@
     android:id="@+id/icon"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
     launcher:iconDisplay="all_apps"
     launcher:centerVertically="true"
     android:paddingLeft="4dp"
index ba7c3f8..5feb42e 100644 (file)
@@ -36,10 +36,6 @@ import com.android.launcher3.util.Themes;
  */
 public class BaseRecyclerViewFastScrollBar {
 
-    public interface FastScrollFocusableView {
-        void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
-    }
-
     private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
             new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
 
index 1a41e08..107d700 100644 (file)
@@ -53,8 +53,7 @@ import java.text.NumberFormat;
  * because we want to make the bubble taller than the text and TextView's clip is
  * too aggressive.
  */
-public class BubbleTextView extends TextView
-        implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView, ItemInfoUpdateReceiver {
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
 
     // Dimensions in DP
     private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
@@ -67,6 +66,8 @@ public class BubbleTextView extends TextView
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
 
+    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
+
     private final Launcher mLauncher;
     private Drawable mIcon;
     private final boolean mCenterVertically;
@@ -232,12 +233,19 @@ public class BubbleTextView extends TextView
     }
 
     @Override
-    public void setPressed(boolean pressed) {
-        super.setPressed(pressed);
-
+    public void refreshDrawableState() {
         if (!mIgnorePressedStateChange) {
-            updateIconState();
+            super.refreshDrawableState();
+        }
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        if (mStayPressed) {
+            mergeDrawableStates(drawableState, STATE_PRESSED);
         }
+        return drawableState;
     }
 
     /** Returns the icon for this view. */
@@ -250,17 +258,6 @@ public class BubbleTextView extends TextView
         return mLayoutHorizontal;
     }
 
-    private void updateIconState() {
-        if (mIcon instanceof FastBitmapDrawable) {
-            FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
-            if (isPressed() || mStayPressed) {
-                d.animateState(FastBitmapDrawable.State.PRESSED);
-            } else {
-                d.animateState(FastBitmapDrawable.State.NORMAL);
-            }
-        }
-    }
-
     @Override
     public void setOnLongClickListener(OnLongClickListener l) {
         super.setOnLongClickListener(l);
@@ -334,7 +331,7 @@ public class BubbleTextView extends TextView
                     this, mPressedBackground);
         }
 
-        updateIconState();
+        refreshDrawableState();
     }
 
     void clearPressedBackground() {
@@ -364,7 +361,7 @@ public class BubbleTextView extends TextView
 
         mPressedBackground = null;
         mIgnorePressedStateChange = false;
-        updateIconState();
+        refreshDrawableState();
         return result;
     }
 
@@ -544,10 +541,6 @@ public class BubbleTextView extends TextView
     @Override
     public void reapplyItemInfo(ItemInfoWithIcon info) {
         if (getTag() == info) {
-            FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
-            if (mIcon instanceof FastBitmapDrawable) {
-                prevState = ((FastBitmapDrawable) mIcon).getCurrentState();
-            }
             mIconLoadRequest = null;
             mDisableRelayout = true;
 
@@ -566,12 +559,6 @@ public class BubbleTextView extends TextView
                 applyFromPackageItemInfo((PackageItemInfo) info);
             }
 
-            // If we are reapplying over an old icon, then we should update the new icon to the same
-            // state as the old icon
-            if (mIcon instanceof FastBitmapDrawable) {
-                ((FastBitmapDrawable) mIcon).setState(prevState);
-            }
-
             mDisableRelayout = false;
         }
     }
@@ -593,34 +580,6 @@ public class BubbleTextView extends TextView
         }
     }
 
-    @Override
-    public void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated) {
-        // We can only set the fast scroll focus state on a FastBitmapDrawable
-        if (!(mIcon instanceof FastBitmapDrawable)) {
-            return;
-        }
-
-        FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
-        if (animated) {
-            FastBitmapDrawable.State prevState = d.getCurrentState();
-            if (d.animateState(focusState)) {
-                // If the state was updated, then update the view accordingly
-                animate().scaleX(focusState.viewScale)
-                        .scaleY(focusState.viewScale)
-                        .setStartDelay(getStartDelayForStateChange(prevState, focusState))
-                        .setDuration(d.getDurationForStateChange(prevState, focusState))
-                        .start();
-            }
-        } else {
-            if (d.setState(focusState)) {
-                // If the state was updated, then update the view accordingly
-                animate().cancel();
-                setScaleX(focusState.viewScale);
-                setScaleY(focusState.viewScale);
-            }
-        }
-    }
-
     /**
      * Returns true if the view can show custom shortcuts.
      */
@@ -630,21 +589,6 @@ public class BubbleTextView extends TextView
     }
 
     /**
-     * Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
-     */
-    private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
-            final FastBitmapDrawable.State toState) {
-        switch (toState) {
-            case NORMAL:
-                switch (fromState) {
-                    case FAST_SCROLL_HIGHLIGHTED:
-                        return FastBitmapDrawable.FAST_SCROLL_INACTIVE_DURATION / 4;
-                }
-        }
-        return 0;
-    }
-
-    /**
      * Interface to be implemented by the grand parent to allow click shadow effect.
      */
     public interface BubbleTextShadowHandler {
index 1f74c88..be3ba90 100644 (file)
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.graphics.Bitmap;
@@ -32,42 +31,19 @@ import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
 import android.util.Property;
 import android.util.SparseArray;
-import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.graphics.IconPalette;
 
 public class FastBitmapDrawable extends Drawable {
+
+    private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
+
+    private static final float PRESSED_BRIGHTNESS = 100f / 255f;
     private static final float DISABLED_DESATURATION = 1f;
     private static final float DISABLED_BRIGHTNESS = 0.5f;
 
-    /**
-     * The possible states that a FastBitmapDrawable can be in.
-     */
-    public enum State {
-
-        NORMAL                      (0f, 0f, 1f, new DecelerateInterpolator()),
-        PRESSED                     (0f, 100f / 255f, 1f, CLICK_FEEDBACK_INTERPOLATOR),
-        FAST_SCROLL_HIGHLIGHTED     (0f, 0f, 1.15f, new DecelerateInterpolator()),
-        FAST_SCROLL_UNHIGHLIGHTED   (0f, 0f, 1f, new DecelerateInterpolator());
-
-        public final float desaturation;
-        public final float brightness;
-        /**
-         * Used specifically by the view drawing this FastBitmapDrawable.
-         */
-        public final float viewScale;
-        public final TimeInterpolator interpolator;
-
-        State(float desaturation, float brightness, float viewScale, TimeInterpolator interpolator) {
-            this.desaturation = desaturation;
-            this.brightness = brightness;
-            this.viewScale = viewScale;
-            this.interpolator = interpolator;
-        }
-    }
-
     public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
 
         @Override
@@ -82,10 +58,6 @@ public class FastBitmapDrawable extends Drawable {
         }
     };
     public static final int CLICK_FEEDBACK_DURATION = 2000;
-    public static final int FAST_SCROLL_HIGHLIGHT_DURATION = 225;
-    public static final int FAST_SCROLL_UNHIGHLIGHT_DURATION = 150;
-    public static final int FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION = 225;
-    public static final int FAST_SCROLL_INACTIVE_DURATION = 275;
 
     // Since we don't need 256^2 values for combinations of both the brightness and saturation, we
     // reduce the value space to a smaller value V, which reduces the number of cached
@@ -101,7 +73,8 @@ public class FastBitmapDrawable extends Drawable {
 
     protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
     private final Bitmap mBitmap;
-    private State mState = State.NORMAL;
+
+    private boolean mIsPressed;
     private boolean mIsDisabled;
 
     private BadgeInfo mBadgeInfo;
@@ -123,6 +96,19 @@ public class FastBitmapDrawable extends Drawable {
         }
     };
 
+    private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
+            = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
+        @Override
+        public Float get(FastBitmapDrawable fastBitmapDrawable) {
+            return fastBitmapDrawable.getBrightness();
+        }
+
+        @Override
+        public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
+            fastBitmapDrawable.setBrightness(value);
+        }
+    };
+
     // The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
     // as a result, can be used to compose the key for the cached ColorMatrixColorFilters
     private int mDesaturation = 0;
@@ -130,8 +116,8 @@ public class FastBitmapDrawable extends Drawable {
     private int mAlpha = 255;
     private int mPrevUpdateKey = Integer.MAX_VALUE;
 
-    // Animators for the fast bitmap drawable's properties
-    private AnimatorSet mPropertyAnimator;
+    // Animators for the fast bitmap drawable's brightness
+    private ObjectAnimator mBrightnessAnimator;
 
     public FastBitmapDrawable(Bitmap b) {
         mBitmap = b;
@@ -243,60 +229,50 @@ public class FastBitmapDrawable extends Drawable {
         return mBitmap;
     }
 
-    /**
-     * Animates this drawable to a new state.
-     *
-     * @return whether the state has changed.
-     */
-    public boolean animateState(State newState) {
-        State prevState = mState;
-        if (mState != newState) {
-            mState = newState;
-
-            float desaturation = mIsDisabled ? DISABLED_DESATURATION : newState.desaturation;
-            float brightness = mIsDisabled ? DISABLED_BRIGHTNESS: newState.brightness;
-
-            mPropertyAnimator = cancelAnimator(mPropertyAnimator);
-            mPropertyAnimator = new AnimatorSet();
-            mPropertyAnimator.playTogether(
-                    ObjectAnimator.ofFloat(this, "desaturation", desaturation),
-                    ObjectAnimator.ofFloat(this, "brightness", brightness));
-            mPropertyAnimator.setInterpolator(newState.interpolator);
-            mPropertyAnimator.setDuration(getDurationForStateChange(prevState, newState));
-            mPropertyAnimator.setStartDelay(getStartDelayForStateChange(prevState, newState));
-            mPropertyAnimator.start();
-            return true;
-        }
-        return false;
+    @Override
+    public boolean isStateful() {
+        return true;
     }
 
-    /**
-     * Immediately sets this drawable to a new state.
-     *
-     * @return whether the state has changed.
-     */
-    public boolean setState(State newState) {
-        if (mState != newState) {
-            mState = newState;
+    @Override
+    protected boolean onStateChange(int[] state) {
+        boolean isPressed = false;
+        for (int s : state) {
+            if (s == android.R.attr.state_pressed) {
+                isPressed = true;
+                break;
+            }
+        }
+        if (mIsPressed != isPressed) {
+            mIsPressed = isPressed;
 
-            mPropertyAnimator = cancelAnimator(mPropertyAnimator);
+            if (mBrightnessAnimator != null) {
+                mBrightnessAnimator.cancel();
+            }
 
-            invalidateDesaturationAndBrightness();
+            if (mIsPressed) {
+                // Animate when going to pressed state
+                mBrightnessAnimator = ObjectAnimator.ofFloat(
+                        this, BRIGHTNESS, getExpectedBrightness());
+                mBrightnessAnimator.setDuration(CLICK_FEEDBACK_DURATION);
+                mBrightnessAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
+                mBrightnessAnimator.start();
+            } else {
+                setBrightness(getExpectedBrightness());
+            }
             return true;
         }
         return false;
     }
 
     private void invalidateDesaturationAndBrightness() {
-        setDesaturation(mIsDisabled ? DISABLED_DESATURATION : mState.desaturation);
-        setBrightness(mIsDisabled ? DISABLED_BRIGHTNESS: mState.brightness);
+        setDesaturation(mIsDisabled ? DISABLED_DESATURATION : 0);
+        setBrightness(getExpectedBrightness());
     }
 
-    /**
-     * Returns the current state.
-     */
-    public State getCurrentState() {
-        return mState;
+    private float getExpectedBrightness() {
+        return mIsDisabled ? DISABLED_BRIGHTNESS :
+                (mIsPressed ? PRESSED_BRIGHTNESS : 0);
     }
 
     public void setIsDisabled(boolean isDisabled) {
@@ -307,49 +283,6 @@ public class FastBitmapDrawable extends Drawable {
     }
 
     /**
-     * Returns the duration for the state change animation.
-     */
-    public static int getDurationForStateChange(State fromState, State toState) {
-        switch (toState) {
-            case NORMAL:
-                switch (fromState) {
-                    case PRESSED:
-                        return 0;
-                    case FAST_SCROLL_HIGHLIGHTED:
-                    case FAST_SCROLL_UNHIGHLIGHTED:
-                        return FAST_SCROLL_INACTIVE_DURATION;
-                }
-            case PRESSED:
-                return CLICK_FEEDBACK_DURATION;
-            case FAST_SCROLL_HIGHLIGHTED:
-                return FAST_SCROLL_HIGHLIGHT_DURATION;
-            case FAST_SCROLL_UNHIGHLIGHTED:
-                switch (fromState) {
-                    case NORMAL:
-                        // When animating from normal state, take a little longer
-                        return FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION;
-                    default:
-                        return FAST_SCROLL_UNHIGHLIGHT_DURATION;
-                }
-        }
-        return 0;
-    }
-
-    /**
-     * Returns the start delay when animating between certain fast scroll states.
-     */
-    public static int getStartDelayForStateChange(State fromState, State toState) {
-        switch (toState) {
-            case FAST_SCROLL_UNHIGHLIGHTED:
-                switch (fromState) {
-                    case NORMAL:
-                        return FAST_SCROLL_UNHIGHLIGHT_DURATION / 4;
-                }
-        }
-        return 0;
-    }
-
-    /**
      * Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
      */
     private void setDesaturation(float desaturation) {
@@ -435,11 +368,4 @@ public class FastBitmapDrawable extends Drawable {
         }
         invalidateSelf();
     }
-
-    private AnimatorSet cancelAnimator(AnimatorSet animator) {
-        if (animator != null) {
-            animator.cancel();
-        }
-        return null;
-    }
 }
index 28b7685..a1ff822 100644 (file)
@@ -19,6 +19,7 @@ import android.support.v7.widget.RecyclerView;
 import android.view.View;
 
 import com.android.launcher3.BaseRecyclerViewFastScrollBar;
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.util.Thunk;
 
@@ -45,8 +46,7 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
 
     // Set of all views animated during fast scroll.  We keep track of these ourselves since there
     // is no way to reset a view once it gets scrapped or recycled without other hacks
-    private HashSet<BaseRecyclerViewFastScrollBar.FastScrollFocusableView> mTrackedFastScrollViews =
-            new HashSet<>();
+    private HashSet<RecyclerView.ViewHolder> mTrackedFastScrollViews = new HashSet<>();
 
     // Smooth fast-scroll animation frames
     @Thunk int mFastScrollFrameIndex;
@@ -186,12 +186,7 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
     public void onBindView(AllAppsGridAdapter.ViewHolder holder) {
         // Update newly bound views to the current fast scroll state if we are fast scrolling
         if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) {
-            if (holder.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
-                BaseRecyclerViewFastScrollBar.FastScrollFocusableView v =
-                        (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) holder.itemView;
-                updateViewFastScrollFocusState(v, holder.getPosition(), false /* animated */);
-                mTrackedFastScrollViews.add(v);
-            }
+            mTrackedFastScrollViews.add(holder);
         }
     }
 
@@ -201,9 +196,9 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
     private void trackAllChildViews() {
         int childCount = mRv.getChildCount();
         for (int i = 0; i < childCount; i++) {
-            View v = mRv.getChildAt(i);
-            if (v instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
-                mTrackedFastScrollViews.add((BaseRecyclerViewFastScrollBar.FastScrollFocusableView) v);
+            RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder(mRv.getChildAt(i));
+            if (viewHolder != null) {
+                mTrackedFastScrollViews.add(viewHolder);
             }
         }
     }
@@ -212,27 +207,16 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb
      * Updates the fast scroll focus on all the children.
      */
     private void updateTrackedViewsFastScrollFocusState() {
-        for (BaseRecyclerViewFastScrollBar.FastScrollFocusableView v : mTrackedFastScrollViews) {
-            RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder((View) v);
-            int pos = (viewHolder != null) ? viewHolder.getPosition() : -1;
-            updateViewFastScrollFocusState(v, pos, true);
-        }
-    }
-
-    /**
-     * Updates the fast scroll focus on all a given view.
-     */
-    private void updateViewFastScrollFocusState(BaseRecyclerViewFastScrollBar.FastScrollFocusableView v,
-                                                int pos, boolean animated) {
-        FastBitmapDrawable.State newState = FastBitmapDrawable.State.NORMAL;
-        if (mCurrentFastScrollSection != null && pos > -1) {
-            AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
-            boolean highlight = item.sectionName.equals(mCurrentFastScrollSection) &&
-                    item.position == mTargetFastScrollPosition;
-            newState = highlight ?
-                    FastBitmapDrawable.State.FAST_SCROLL_HIGHLIGHTED :
-                    FastBitmapDrawable.State.FAST_SCROLL_UNHIGHLIGHTED;
+        for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
+            int pos = viewHolder.getAdapterPosition();
+            boolean isActive = false;
+            if (mCurrentFastScrollSection != null && pos > -1) {
+                AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
+                isActive = item != null &&
+                        mCurrentFastScrollSection.equals(item.sectionName) &&
+                        item.position == mTargetFastScrollPosition;
+            }
+            viewHolder.itemView.setActivated(isActive);
         }
-        v.setFastScrollFocusState(newState, animated);
     }
 }