From 5d9fb0e92f2ef2b25f3aa93c1f89838ccc68aa88 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Sat, 8 Oct 2016 17:43:48 -0700 Subject: [PATCH] Simplifying fast scroller logic > Using a separate view for drawing the popup. This allows us to use elevation property instead of drawing the shadow as bitmap. > During the thumb animation, invalidating the full track width, instead of invalidating the track and thumb separately. > The thumb path is calculated at 0,0 and drawn using canvas.translate(). This avoids recalculating the path on every scroll. Change-Id: I48741e5b4432df0d939016db284d7aaf52cc2aa6 --- res/layout/all_apps.xml | 16 + res/layout/widgets_view.xml | 16 + res/values/dimens.xml | 1 + .../android/launcher3/BaseRecyclerView.java | 25 +- .../BaseRecyclerViewFastScrollBar.java | 276 +++++++++--------- .../BaseRecyclerViewFastScrollPopup.java | 198 ------------- .../allapps/AllAppsContainerView.java | 2 +- .../allapps/AllAppsRecyclerView.java | 13 +- .../graphics/HolographicOutlineHelper.java | 22 +- .../launcher3/widget/WidgetsRecyclerView.java | 2 +- 10 files changed, 203 insertions(+), 368 deletions(-) delete mode 100644 src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index 926883927f..d48bd500e6 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -55,6 +55,22 @@ android:focusable="true" android:theme="@style/CustomOverscroll.Light" /> + + + + + + 5dp 9dp + 18dp 72dp -24dp 72dp diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 45bc940064..9bdbe2580a 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -22,6 +22,8 @@ import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.ViewGroup; + import com.android.launcher3.util.Thunk; @@ -41,7 +43,7 @@ public abstract class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; - protected BaseRecyclerViewFastScrollBar mScrollbar; + protected final BaseRecyclerViewFastScrollBar mScrollbar; private int mDownX; private int mDownY; @@ -92,6 +94,12 @@ public abstract class BaseRecyclerView extends RecyclerView addOnItemTouchListener(this); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup)); + } + /** * We intercept the touch handling only to support fast scrolling when initiated from the * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling. @@ -235,7 +243,7 @@ public abstract class BaseRecyclerView extends RecyclerView // Only show the scrollbar if there is height to be scrolled int availableScrollBarHeight = getAvailableScrollBarHeight(); if (availableScrollHeight <= 0) { - mScrollbar.setThumbOffset(-1, -1); + mScrollbar.setThumbOffsetY(-1); return; } @@ -246,18 +254,7 @@ public abstract class BaseRecyclerView extends RecyclerView (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); // Calculate the position and size of the scroll bar - mScrollbar.setThumbOffset(getScrollBarX(), scrollBarY); - } - - /** - * @return the x position for the scrollbar thumb - */ - protected int getScrollBarX() { - if (Utilities.isRtl(getResources())) { - return mBackgroundPadding.left; - } else { - return getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth(); - } + mScrollbar.setThumbOffsetY(scrollBarY); } /** diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java index 3d71632ce7..770166daea 100644 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java @@ -15,21 +15,18 @@ */ package com.android.launcher3; -import android.animation.AnimatorSet; -import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.Point; import android.graphics.Rect; +import android.util.Property; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; - -import com.android.launcher3.util.Thunk; +import android.widget.TextView; /** * The track and scrollbar that shows when you scroll the list. @@ -40,29 +37,46 @@ public class BaseRecyclerViewFastScrollBar { void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated); } + private static final Property TRACK_WIDTH = + new Property(Integer.class, "width") { + + @Override + public Integer get(BaseRecyclerViewFastScrollBar scrollBar) { + return scrollBar.mWidth; + } + + @Override + public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) { + scrollBar.setTrackWidth(value); + } + }; + private final static int MAX_TRACK_ALPHA = 30; private final static int SCROLL_BAR_VIS_DURATION = 150; + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - @Thunk BaseRecyclerView mRv; - private BaseRecyclerViewFastScrollPopup mPopup; + private final Rect mTmpRect = new Rect(); + private final BaseRecyclerView mRv; - private AnimatorSet mScrollbarAnimator; + private final boolean mIsRtl; - private int mThumbInactiveColor; - private int mThumbActiveColor; - @Thunk Point mThumbOffset = new Point(-1, -1); - @Thunk Paint mThumbPaint; - private int mThumbMinWidth; - private int mThumbMaxWidth; - @Thunk int mThumbWidth; - @Thunk int mThumbHeight; - private int mThumbCurvature; - private Path mThumbPath = new Path(); - private Paint mTrackPaint; - private int mTrackWidth; - private float mLastTouchY; // The inset is the buffer around which a point will still register as a click on the scrollbar - private int mTouchInset; + private final int mTouchInset; + + private final int mMinWidth; + private final int mMaxWidth; + + // Current width of the track + private int mWidth; + private ObjectAnimator mWidthAnimator; + + private final Path mThumbPath = new Path(); + private final Paint mThumbPaint; + private final int mThumbHeight; + + private final Paint mTrackPaint; + + private float mLastTouchY; private boolean mIsDragging; private boolean mIsThumbDetached; private boolean mCanThumbDetach; @@ -70,27 +84,35 @@ public class BaseRecyclerViewFastScrollBar { // This is the offset from the top of the scrollbar when the user first starts touching. To // prevent jumping, this offset is applied as the user scrolls. - private int mTouchOffset; + private int mTouchOffsetY; + private int mThumbOffsetY; - private Rect mInvalidateRect = new Rect(); - private Rect mTmpRect = new Rect(); + // Fast scroller popup + private TextView mPopupView; + private boolean mPopupVisible; + private String mPopupSectionName; public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) { mRv = rv; - mPopup = new BaseRecyclerViewFastScrollPopup(rv, res); mTrackPaint = new Paint(); mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK)); mTrackPaint.setAlpha(MAX_TRACK_ALPHA); - mThumbActiveColor = mThumbInactiveColor = Utilities.getColorAccent(rv.getContext()); + mThumbPaint = new Paint(); mThumbPaint.setAntiAlias(true); - mThumbPaint.setColor(mThumbInactiveColor); + mThumbPaint.setColor(Utilities.getColorAccent(rv.getContext())); mThumbPaint.setStyle(Paint.Style.FILL); - mThumbWidth = mThumbMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width); - mThumbMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width); + + mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width); + mMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width); mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height); - mThumbCurvature = mThumbMaxWidth - mThumbMinWidth; mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset); + mIsRtl = Utilities.isRtl(res); + updateThumbPath(); + } + + public void setPopupView(View popup) { + mPopupView = (TextView) popup; } public void setDetachThumbOnFastScroll() { @@ -101,51 +123,54 @@ public class BaseRecyclerViewFastScrollBar { mIsThumbDetached = false; } - public void setThumbOffset(int x, int y) { - if (mThumbOffset.x == x && mThumbOffset.y == y) { + private int getDrawLeft() { + return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth); + } + + public void setThumbOffsetY(int y) { + if (mThumbOffsetY == y) { return; } - mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, - mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); - mThumbOffset.set(x, y); + + // Invalidate the previous and new thumb area + int drawLeft = getDrawLeft(); + mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight); + mThumbOffsetY = y; + mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight); + mRv.invalidate(mTmpRect); + } + + public int getThumbOffsetY() { + return mThumbOffsetY; + } + + private void setTrackWidth(int width) { + if (mWidth == width) { + return; + } + int left = getDrawLeft(); + // Invalidate the whole scroll bar area. + mRv.invalidate(left, 0, left + mMaxWidth, mRv.getVisibleHeight()); + + mWidth = width; updateThumbPath(); - mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, - mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); - mRv.invalidate(mInvalidateRect); } - public Point getThumbOffset() { - return mThumbOffset; - } + /** + * Updates the path for the thumb drawable. + */ + private void updateThumbPath() { + int smallWidth = mIsRtl ? mWidth : -mWidth; + int largeWidth = mIsRtl ? mMaxWidth : -mMaxWidth; - // Setter/getter for the thumb bar width for animations - public void setThumbWidth(int width) { - mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, - mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); - mThumbWidth = width; - updateThumbPath(); - mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, - mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); - mRv.invalidate(mInvalidateRect); - } - - public int getThumbWidth() { - return mThumbWidth; - } - - // Setter/getter for the track bar width for animations - public void setTrackWidth(int width) { - mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth, - mRv.getVisibleHeight()); - mTrackWidth = width; - updateThumbPath(); - mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth, - mRv.getVisibleHeight()); - mRv.invalidate(mInvalidateRect); - } - - public int getTrackWidth() { - return mTrackWidth; + mThumbPath.reset(); + mThumbPath.moveTo(0, 0); + mThumbPath.lineTo(0, mThumbHeight); // Left edge + mThumbPath.lineTo(smallWidth, mThumbHeight); // bottom edge + mThumbPath.cubicTo(smallWidth, mThumbHeight, // right edge + largeWidth, mThumbHeight / 2, + smallWidth, 0); + mThumbPath.close(); } public int getThumbHeight() { @@ -153,7 +178,7 @@ public class BaseRecyclerViewFastScrollBar { } public int getThumbMaxWidth() { - return mThumbMaxWidth; + return mMaxWidth; } public boolean isDraggingThumb() { @@ -176,7 +201,7 @@ public class BaseRecyclerViewFastScrollBar { switch (action) { case MotionEvent.ACTION_DOWN: if (isNearThumb(downX, downY)) { - mTouchOffset = downY - mThumbOffset.y; + mTouchOffsetY = downY - mThumbOffsetY; } break; case MotionEvent.ACTION_MOVE: @@ -191,32 +216,35 @@ public class BaseRecyclerViewFastScrollBar { if (mCanThumbDetach) { mIsThumbDetached = true; } - mTouchOffset += (lastY - downY); - mPopup.animateVisibility(true); + mTouchOffsetY += (lastY - downY); + animatePopupVisibility(true); showActiveScrollbar(true); } if (mIsDragging) { // Update the fastscroller section name at this touch position int top = mRv.getBackgroundPadding().top; int bottom = top + mRv.getVisibleHeight() - mThumbHeight; - float boundedY = (float) Math.max(top, Math.min(bottom, y - mTouchOffset)); + float boundedY = (float) Math.max(top, Math.min(bottom, y - mTouchOffsetY)); String sectionName = mRv.scrollToPositionAtProgress((boundedY - top) / (bottom - top)); - mPopup.setSectionName(sectionName); - mPopup.animateVisibility(!sectionName.isEmpty()); - mRv.invalidate(mPopup.updateFastScrollerBounds(lastY)); + if (!sectionName.equals(mPopupSectionName)) { + mPopupSectionName = sectionName; + mPopupView.setText(sectionName); + } + animatePopupVisibility(!sectionName.isEmpty()); + updatePopupY(lastY); mLastTouchY = boundedY; - setThumbOffset(mRv.getScrollBarX(), (int) mLastTouchY); + setThumbOffsetY((int) mLastTouchY); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - mTouchOffset = 0; + mTouchOffsetY = 0; mLastTouchY = 0; mIgnoreDragGesture = false; if (mIsDragging) { mIsDragging = false; - mPopup.animateVisibility(false); + animatePopupVisibility(false); showActiveScrollbar(false); } break; @@ -224,74 +252,58 @@ public class BaseRecyclerViewFastScrollBar { } public void draw(Canvas canvas) { - if (mThumbOffset.x < 0 || mThumbOffset.y < 0) { + if (mThumbOffsetY < 0) { return; } - - // Draw the scroll bar track and thumb - if (mTrackPaint.getAlpha() > 0) { - canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, - mRv.getVisibleHeight(), mTrackPaint); + int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + if (!mIsRtl) { + canvas.translate(mRv.getWidth(), 0); } - canvas.drawPath(mThumbPath, mThumbPaint); + // Draw the track + int thumbWidth = mIsRtl ? mWidth : -mWidth; + canvas.drawRect(0, 0, thumbWidth, mRv.getVisibleHeight(), mTrackPaint); - // Draw the popup - mPopup.draw(canvas); + canvas.translate(0, mThumbOffsetY); + canvas.drawPath(mThumbPath, mThumbPaint); + canvas.restoreToCount(saveCount); } /** - * Animates the width and color of the scrollbar. + * Animates the width of the scrollbar. */ private void showActiveScrollbar(boolean isScrolling) { - if (mScrollbarAnimator != null) { - mScrollbarAnimator.cancel(); + if (mWidthAnimator != null) { + mWidthAnimator.cancel(); } - mScrollbarAnimator = new AnimatorSet(); - ObjectAnimator trackWidthAnim = ObjectAnimator.ofInt(this, "trackWidth", - isScrolling ? mThumbMaxWidth : mThumbMinWidth); - ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "thumbWidth", - isScrolling ? mThumbMaxWidth : mThumbMinWidth); - mScrollbarAnimator.playTogether(trackWidthAnim, thumbWidthAnim); - if (mThumbActiveColor != mThumbInactiveColor) { - ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), - mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor); - colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animator) { - mThumbPaint.setColor((Integer) animator.getAnimatedValue()); - mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, - mThumbOffset.y + mThumbHeight); - } - }); - mScrollbarAnimator.play(colorAnimation); - } - mScrollbarAnimator.setDuration(SCROLL_BAR_VIS_DURATION); - mScrollbarAnimator.start(); - } - - /** - * Updates the path for the thumb drawable. - */ - private void updateThumbPath() { - mThumbCurvature = mThumbMaxWidth - mThumbWidth; - mThumbPath.reset(); - mThumbPath.moveTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y); // tr - mThumbPath.lineTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); // br - mThumbPath.lineTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight); // bl - mThumbPath.cubicTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight, - mThumbOffset.x - mThumbCurvature, mThumbOffset.y + mThumbHeight / 2, - mThumbOffset.x, mThumbOffset.y); // bl2tl - mThumbPath.close(); + mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH, + isScrolling ? mMaxWidth : mMinWidth); + mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION); + mWidthAnimator.start(); } /** * Returns whether the specified points are near the scroll bar bounds. */ public boolean isNearThumb(int x, int y) { - mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, - mThumbOffset.y + mThumbHeight); + int left = getDrawLeft(); + mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight); mTmpRect.inset(mTouchInset, mTouchInset); return mTmpRect.contains(x, y); } + + private void animatePopupVisibility(boolean visible) { + if (mPopupVisible != visible) { + mPopupVisible = visible; + mPopupView.animate().cancel(); + mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start(); + } + } + + private void updatePopupY(int lastTouchY) { + int height = mPopupView.getHeight(); + float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height); + top = Math.max(mMaxWidth, Math.min(top, mRv.getVisibleHeight() - mMaxWidth - height)); + mPopupView.setTranslationY(top); + } } diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java deleted file mode 100644 index b9b044db9d..0000000000 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ -package com.android.launcher3; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; - -import com.android.launcher3.graphics.HolographicOutlineHelper; - -/** - * The fast scroller popup that shows the section name the list will jump to. - */ -public class BaseRecyclerViewFastScrollPopup { - - private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - - private static final int SHADOW_INSET = 3; - private static final int SHADOW_SHIFT_Y = 2; - private static final float SHADOW_ALPHA_MULTIPLIER = 0.67f; - - private Resources mRes; - private BaseRecyclerView mRv; - - private Bitmap mShadow; - private Paint mShadowPaint; - - private Drawable mBg; - // The absolute bounds of the fast scroller bg - private Rect mBgBounds = new Rect(); - private int mBgOriginalSize; - private Rect mInvalidateRect = new Rect(); - private Rect mTmpRect = new Rect(); - - private String mSectionName; - private Paint mTextPaint; - private Rect mTextBounds = new Rect(); - private float mAlpha; - - private Animator mAlphaAnimator; - private boolean mVisible; - - public BaseRecyclerViewFastScrollPopup(BaseRecyclerView rv, Resources res) { - mRes = res; - mRv = rv; - - mBgOriginalSize = res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_size); - mBg = rv.getContext().getDrawable(R.drawable.container_fastscroll_popup_bg); - mBg.setBounds(0, 0, mBgOriginalSize, mBgOriginalSize); - - mTextPaint = new Paint(); - mTextPaint.setColor(Color.WHITE); - mTextPaint.setAntiAlias(true); - mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_text_size)); - - mShadowPaint = new Paint(); - mShadowPaint.setAntiAlias(true); - mShadowPaint.setFilterBitmap(true); - mShadowPaint.setDither(true); - } - - /** - * Sets the section name. - */ - public void setSectionName(String sectionName) { - if (!sectionName.equals(mSectionName)) { - mSectionName = sectionName; - mTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTextBounds); - // Update the width to use measureText since that is more accurate - mTextBounds.right = (int) (mTextBounds.left + mTextPaint.measureText(sectionName)); - } - } - - /** - * Updates the bounds for the fast scroller. - * - * @return the invalidation rect for this update. - */ - public Rect updateFastScrollerBounds(int lastTouchY) { - mInvalidateRect.set(mBgBounds); - - if (isVisible()) { - // Calculate the dimensions and position of the fast scroller popup - int edgePadding = mRv.getMaxScrollbarWidth(); - int bgPadding = (mBgOriginalSize - mTextBounds.height()) / 2; - int bgHeight = mBgOriginalSize; - int bgWidth = Math.max(mBgOriginalSize, mTextBounds.width() + (2 * bgPadding)); - if (Utilities.isRtl(mRes)) { - mBgBounds.left = mRv.getBackgroundPadding().left + (2 * mRv.getMaxScrollbarWidth()); - mBgBounds.right = mBgBounds.left + bgWidth; - } else { - mBgBounds.right = mRv.getWidth() - mRv.getBackgroundPadding().right - - (2 * mRv.getMaxScrollbarWidth()); - mBgBounds.left = mBgBounds.right - bgWidth; - } - mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight); - mBgBounds.top = Math.max(edgePadding, - Math.min(mBgBounds.top, mRv.getVisibleHeight() - edgePadding - bgHeight)); - mBgBounds.bottom = mBgBounds.top + bgHeight; - - // Generate a bitmap for a shadow matching these bounds - mShadow = HolographicOutlineHelper.getInstance( - mRv.getContext()).createMediumDropShadow(mBg, false /* shouldCache */); - } else { - mShadow = null; - mBgBounds.setEmpty(); - } - - // Combine the old and new fast scroller bounds to create the full invalidate rect - mInvalidateRect.union(mBgBounds); - return mInvalidateRect; - } - - /** - * Animates the visibility of the fast scroller popup. - */ - public void animateVisibility(boolean visible) { - if (mVisible != visible) { - mVisible = visible; - if (mAlphaAnimator != null) { - mAlphaAnimator.cancel(); - } - mAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", visible ? 1f : 0f); - mAlphaAnimator.setDuration(visible ? 200 : 150); - mAlphaAnimator.start(); - } - } - - // Setter/getter for the popup alpha for animations - public void setAlpha(float alpha) { - mAlpha = alpha; - mRv.invalidate(mBgBounds); - } - - public float getAlpha() { - return mAlpha; - } - - public int getHeight() { - return mBgOriginalSize; - } - - public void draw(Canvas c) { - if (isVisible()) { - // Determine the alpha and prepare the canvas - final int alpha = (int) (mAlpha * 255); - int restoreCount = c.save(Canvas.MATRIX_SAVE_FLAG); - c.translate(mBgBounds.left, mBgBounds.top); - mTmpRect.set(mBgBounds); - mTmpRect.offsetTo(0, 0); - - // Expand the rect (with a negative inset), translate it, and draw the shadow - if (mShadow != null) { - mTmpRect.inset(-SHADOW_INSET * 2, -SHADOW_INSET * 2); - mTmpRect.offset(0, SHADOW_SHIFT_Y); - mShadowPaint.setAlpha((int) (alpha * SHADOW_ALPHA_MULTIPLIER)); - c.drawBitmap(mShadow, null, mTmpRect, mShadowPaint); - mTmpRect.inset(SHADOW_INSET * 2, SHADOW_INSET * 2); - mTmpRect.offset(0, -SHADOW_SHIFT_Y); - } - - // Draw the background - mBg.setBounds(mTmpRect); - mBg.setAlpha(alpha); - mBg.draw(c); - - // Draw the text - mTextPaint.setAlpha(alpha); - c.drawText(mSectionName, (mBgBounds.width() - mTextBounds.width()) / 2, - mBgBounds.height() - (mBgBounds.height() / 2) - mTextBounds.exactCenterY(), - mTextPaint); - c.restoreToCount(restoreCount); - } - } - - public boolean isVisible() { - return (mAlpha > 0f) && (mSectionName != null); - } -} diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 689ee38168..5c9e3f74e2 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -272,7 +272,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // IF scroller is at the very top OR there is no scroll bar because there is probably not // enough items to scroll, THEN it's okay for the container to be pulled down. - if (mAppsRecyclerView.getScrollBar().getThumbOffset().y <= 0) { + if (mAppsRecyclerView.getScrollBar().getThumbOffsetY() <= 0) { return true; } return false; diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 0173847e05..5a9ea6bb36 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -299,14 +299,14 @@ public class AllAppsRecyclerView extends BaseRecyclerView { // Skip early if there are no items or we haven't been measured if (items.isEmpty() || mNumAppsPerRow == 0) { - mScrollbar.setThumbOffset(-1, -1); + mScrollbar.setThumbOffsetY(-1); return; } // Skip early if, there no child laid out in the container. int scrollY = getCurrentScrollY(); if (scrollY < 0) { - mScrollbar.setThumbOffset(-1, -1); + mScrollbar.setThumbOffsetY(-1); return; } @@ -314,7 +314,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { int availableScrollBarHeight = getAvailableScrollBarHeight(); int availableScrollHeight = getAvailableScrollHeight(); if (availableScrollHeight <= 0) { - mScrollbar.setThumbOffset(-1, -1); + mScrollbar.setThumbOffsetY(-1); return; } @@ -323,11 +323,10 @@ public class AllAppsRecyclerView extends BaseRecyclerView { // Calculate the current scroll position, the scrollY of the recycler view accounts // for the view padding, while the scrollBarY is drawn right up to the background // padding (ignoring padding) - int scrollBarX = getScrollBarX(); int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); - int thumbScrollY = mScrollbar.getThumbOffset().y; + int thumbScrollY = mScrollbar.getThumbOffsetY(); int diffScrollY = scrollBarY - thumbScrollY; if (diffScrollY * dy > 0f) { // User is scrolling in the same direction the thumb needs to catch up to the @@ -344,7 +343,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { thumbScrollY += Math.min(offset, diffScrollY); } thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY)); - mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); + mScrollbar.setThumbOffsetY(thumbScrollY); if (scrollBarY == thumbScrollY) { mScrollbar.reattachThumbToScroll(); } @@ -352,7 +351,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { // User is scrolling in an opposite direction to the direction that the thumb // needs to catch up to the scroll position. Do nothing except for updating // the scroll bar x to match the thumb width. - mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); + mScrollbar.setThumbOffsetY(thumbScrollY); } } } else { diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java index 0d70bdee1f..9c397210e1 100644 --- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java +++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java @@ -158,18 +158,13 @@ public class HolographicOutlineHelper { } public Bitmap createMediumDropShadow(BubbleTextView view) { - return createMediumDropShadow(view.getIcon(), view.getScaleX(), view.getScaleY(), true); - } - - public Bitmap createMediumDropShadow(Drawable drawable, boolean shouldCache) { - return createMediumDropShadow(drawable, 1f, 1f, shouldCache); - } - - Bitmap createMediumDropShadow(Drawable drawable, float scaleX, float scaleY, - boolean shouldCache) { + Drawable drawable = view.getIcon(); if (drawable == null) { return null; } + + float scaleX = view.getScaleX(); + float scaleY = view.getScaleY(); Rect rect = drawable.getBounds(); int bitmapWidth = (int) (rect.width() * scaleX); @@ -179,14 +174,11 @@ public class HolographicOutlineHelper { } int key = (bitmapWidth << 16) | bitmapHeight; - Bitmap cache = shouldCache ? mBitmapCache.get(key) : null; + Bitmap cache = mBitmapCache.get(key); if (cache == null) { cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8); mCanvas.setBitmap(cache); - - if (shouldCache) { - mBitmapCache.put(key, cache); - } + mBitmapCache.put(key, cache); } else { mCanvas.setBitmap(cache); mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); @@ -206,7 +198,7 @@ public class HolographicOutlineHelper { int resultWidth = bitmapWidth + extraSize; int resultHeight = bitmapHeight + extraSize; key = (resultWidth << 16) | resultHeight; - Bitmap result = shouldCache ? mBitmapCache.get(key) : null; + Bitmap result = mBitmapCache.get(key); if (result == null) { result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8); mCanvas.setBitmap(result); diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 2560661c18..c33978eea8 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -121,7 +121,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView { // Skip early if, there no child laid out in the container. int scrollY = getCurrentScrollY(); if (scrollY < 0) { - mScrollbar.setThumbOffset(-1, -1); + mScrollbar.setThumbOffsetY(-1); return; }