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
This commit is contained in:
parent
631ffbda64
commit
5d9fb0e92f
|
@ -55,6 +55,22 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:theme="@style/CustomOverscroll.Light" />
|
android:theme="@style/CustomOverscroll.Light" />
|
||||||
|
|
||||||
|
<!-- Fast scroller popup -->
|
||||||
|
<TextView
|
||||||
|
android:background="@drawable/container_fastscroll_popup_bg"
|
||||||
|
android:layout_width="@dimen/container_fastscroll_popup_size"
|
||||||
|
android:layout_height="@dimen/container_fastscroll_popup_size"
|
||||||
|
android:textSize="@dimen/container_fastscroll_popup_text_size"
|
||||||
|
android:layout_below="@+id/search_container"
|
||||||
|
android:id="@+id/fast_scroller_popup"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:gravity="center"
|
||||||
|
android:alpha="0"
|
||||||
|
android:elevation="3dp"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:layout_marginEnd="@dimen/container_fastscroll_popup_margin"
|
||||||
|
android:textColor="@android:color/white" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/search_container"
|
android:id="@+id/search_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -49,6 +49,22 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<!-- Fast scroller popup -->
|
||||||
|
<TextView
|
||||||
|
android:background="@drawable/container_fastscroll_popup_bg"
|
||||||
|
android:layout_width="@dimen/container_fastscroll_popup_size"
|
||||||
|
android:layout_height="@dimen/container_fastscroll_popup_size"
|
||||||
|
android:textSize="@dimen/container_fastscroll_popup_text_size"
|
||||||
|
android:layout_below="@+id/search_container"
|
||||||
|
android:id="@+id/fast_scroller_popup"
|
||||||
|
android:elevation="3dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:alpha="0"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_marginEnd="@dimen/container_fastscroll_popup_margin"
|
||||||
|
android:textColor="@android:color/white" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
|
|
||||||
<dimen name="container_fastscroll_thumb_min_width">5dp</dimen>
|
<dimen name="container_fastscroll_thumb_min_width">5dp</dimen>
|
||||||
<dimen name="container_fastscroll_thumb_max_width">9dp</dimen>
|
<dimen name="container_fastscroll_thumb_max_width">9dp</dimen>
|
||||||
|
<dimen name="container_fastscroll_popup_margin">18dp</dimen>
|
||||||
<dimen name="container_fastscroll_thumb_height">72dp</dimen>
|
<dimen name="container_fastscroll_thumb_height">72dp</dimen>
|
||||||
<dimen name="container_fastscroll_thumb_touch_inset">-24dp</dimen>
|
<dimen name="container_fastscroll_thumb_touch_inset">-24dp</dimen>
|
||||||
<dimen name="container_fastscroll_popup_size">72dp</dimen>
|
<dimen name="container_fastscroll_popup_size">72dp</dimen>
|
||||||
|
|
|
@ -22,6 +22,8 @@ import android.graphics.Rect;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +43,7 @@ public abstract class BaseRecyclerView extends RecyclerView
|
||||||
@Thunk int mDy = 0;
|
@Thunk int mDy = 0;
|
||||||
private float mDeltaThreshold;
|
private float mDeltaThreshold;
|
||||||
|
|
||||||
protected BaseRecyclerViewFastScrollBar mScrollbar;
|
protected final BaseRecyclerViewFastScrollBar mScrollbar;
|
||||||
|
|
||||||
private int mDownX;
|
private int mDownX;
|
||||||
private int mDownY;
|
private int mDownY;
|
||||||
|
@ -92,6 +94,12 @@ public abstract class BaseRecyclerView extends RecyclerView
|
||||||
addOnItemTouchListener(this);
|
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
|
* 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.
|
* 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
|
// Only show the scrollbar if there is height to be scrolled
|
||||||
int availableScrollBarHeight = getAvailableScrollBarHeight();
|
int availableScrollBarHeight = getAvailableScrollBarHeight();
|
||||||
if (availableScrollHeight <= 0) {
|
if (availableScrollHeight <= 0) {
|
||||||
mScrollbar.setThumbOffset(-1, -1);
|
mScrollbar.setThumbOffsetY(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,18 +254,7 @@ public abstract class BaseRecyclerView extends RecyclerView
|
||||||
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
|
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
|
||||||
|
|
||||||
// Calculate the position and size of the scroll bar
|
// Calculate the position and size of the scroll bar
|
||||||
mScrollbar.setThumbOffset(getScrollBarX(), scrollBarY);
|
mScrollbar.setThumbOffsetY(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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,21 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package com.android.launcher3;
|
package com.android.launcher3;
|
||||||
|
|
||||||
import android.animation.AnimatorSet;
|
|
||||||
import android.animation.ArgbEvaluator;
|
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.util.Property;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
|
import android.widget.TextView;
|
||||||
import com.android.launcher3.util.Thunk;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The track and scrollbar that shows when you scroll the list.
|
* 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);
|
void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
|
||||||
|
new Property<BaseRecyclerViewFastScrollBar, Integer>(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 MAX_TRACK_ALPHA = 30;
|
||||||
private final static int SCROLL_BAR_VIS_DURATION = 150;
|
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 final Rect mTmpRect = new Rect();
|
||||||
private BaseRecyclerViewFastScrollPopup mPopup;
|
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
|
// 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 mIsDragging;
|
||||||
private boolean mIsThumbDetached;
|
private boolean mIsThumbDetached;
|
||||||
private boolean mCanThumbDetach;
|
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
|
// 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.
|
// prevent jumping, this offset is applied as the user scrolls.
|
||||||
private int mTouchOffset;
|
private int mTouchOffsetY;
|
||||||
|
private int mThumbOffsetY;
|
||||||
|
|
||||||
private Rect mInvalidateRect = new Rect();
|
// Fast scroller popup
|
||||||
private Rect mTmpRect = new Rect();
|
private TextView mPopupView;
|
||||||
|
private boolean mPopupVisible;
|
||||||
|
private String mPopupSectionName;
|
||||||
|
|
||||||
public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
|
public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
|
||||||
mRv = rv;
|
mRv = rv;
|
||||||
mPopup = new BaseRecyclerViewFastScrollPopup(rv, res);
|
|
||||||
mTrackPaint = new Paint();
|
mTrackPaint = new Paint();
|
||||||
mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
|
mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
|
||||||
mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
|
mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
|
||||||
mThumbActiveColor = mThumbInactiveColor = Utilities.getColorAccent(rv.getContext());
|
|
||||||
mThumbPaint = new Paint();
|
mThumbPaint = new Paint();
|
||||||
mThumbPaint.setAntiAlias(true);
|
mThumbPaint.setAntiAlias(true);
|
||||||
mThumbPaint.setColor(mThumbInactiveColor);
|
mThumbPaint.setColor(Utilities.getColorAccent(rv.getContext()));
|
||||||
mThumbPaint.setStyle(Paint.Style.FILL);
|
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);
|
mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height);
|
||||||
mThumbCurvature = mThumbMaxWidth - mThumbMinWidth;
|
|
||||||
mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset);
|
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() {
|
public void setDetachThumbOnFastScroll() {
|
||||||
|
@ -101,51 +123,54 @@ public class BaseRecyclerViewFastScrollBar {
|
||||||
mIsThumbDetached = false;
|
mIsThumbDetached = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setThumbOffset(int x, int y) {
|
private int getDrawLeft() {
|
||||||
if (mThumbOffset.x == x && mThumbOffset.y == y) {
|
return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThumbOffsetY(int y) {
|
||||||
|
if (mThumbOffsetY == y) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
|
|
||||||
mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
|
// Invalidate the previous and new thumb area
|
||||||
mThumbOffset.set(x, y);
|
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();
|
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
|
mThumbPath.reset();
|
||||||
public void setThumbWidth(int width) {
|
mThumbPath.moveTo(0, 0);
|
||||||
mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
|
mThumbPath.lineTo(0, mThumbHeight); // Left edge
|
||||||
mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
|
mThumbPath.lineTo(smallWidth, mThumbHeight); // bottom edge
|
||||||
mThumbWidth = width;
|
mThumbPath.cubicTo(smallWidth, mThumbHeight, // right edge
|
||||||
updateThumbPath();
|
largeWidth, mThumbHeight / 2,
|
||||||
mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
|
smallWidth, 0);
|
||||||
mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
|
mThumbPath.close();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getThumbHeight() {
|
public int getThumbHeight() {
|
||||||
|
@ -153,7 +178,7 @@ public class BaseRecyclerViewFastScrollBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getThumbMaxWidth() {
|
public int getThumbMaxWidth() {
|
||||||
return mThumbMaxWidth;
|
return mMaxWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDraggingThumb() {
|
public boolean isDraggingThumb() {
|
||||||
|
@ -176,7 +201,7 @@ public class BaseRecyclerViewFastScrollBar {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
if (isNearThumb(downX, downY)) {
|
if (isNearThumb(downX, downY)) {
|
||||||
mTouchOffset = downY - mThumbOffset.y;
|
mTouchOffsetY = downY - mThumbOffsetY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
@ -191,32 +216,35 @@ public class BaseRecyclerViewFastScrollBar {
|
||||||
if (mCanThumbDetach) {
|
if (mCanThumbDetach) {
|
||||||
mIsThumbDetached = true;
|
mIsThumbDetached = true;
|
||||||
}
|
}
|
||||||
mTouchOffset += (lastY - downY);
|
mTouchOffsetY += (lastY - downY);
|
||||||
mPopup.animateVisibility(true);
|
animatePopupVisibility(true);
|
||||||
showActiveScrollbar(true);
|
showActiveScrollbar(true);
|
||||||
}
|
}
|
||||||
if (mIsDragging) {
|
if (mIsDragging) {
|
||||||
// Update the fastscroller section name at this touch position
|
// Update the fastscroller section name at this touch position
|
||||||
int top = mRv.getBackgroundPadding().top;
|
int top = mRv.getBackgroundPadding().top;
|
||||||
int bottom = top + mRv.getVisibleHeight() - mThumbHeight;
|
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) /
|
String sectionName = mRv.scrollToPositionAtProgress((boundedY - top) /
|
||||||
(bottom - top));
|
(bottom - top));
|
||||||
mPopup.setSectionName(sectionName);
|
if (!sectionName.equals(mPopupSectionName)) {
|
||||||
mPopup.animateVisibility(!sectionName.isEmpty());
|
mPopupSectionName = sectionName;
|
||||||
mRv.invalidate(mPopup.updateFastScrollerBounds(lastY));
|
mPopupView.setText(sectionName);
|
||||||
|
}
|
||||||
|
animatePopupVisibility(!sectionName.isEmpty());
|
||||||
|
updatePopupY(lastY);
|
||||||
mLastTouchY = boundedY;
|
mLastTouchY = boundedY;
|
||||||
setThumbOffset(mRv.getScrollBarX(), (int) mLastTouchY);
|
setThumbOffsetY((int) mLastTouchY);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
mTouchOffset = 0;
|
mTouchOffsetY = 0;
|
||||||
mLastTouchY = 0;
|
mLastTouchY = 0;
|
||||||
mIgnoreDragGesture = false;
|
mIgnoreDragGesture = false;
|
||||||
if (mIsDragging) {
|
if (mIsDragging) {
|
||||||
mIsDragging = false;
|
mIsDragging = false;
|
||||||
mPopup.animateVisibility(false);
|
animatePopupVisibility(false);
|
||||||
showActiveScrollbar(false);
|
showActiveScrollbar(false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -224,74 +252,58 @@ public class BaseRecyclerViewFastScrollBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
if (mThumbOffset.x < 0 || mThumbOffset.y < 0) {
|
if (mThumbOffsetY < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
|
||||||
// Draw the scroll bar track and thumb
|
if (!mIsRtl) {
|
||||||
if (mTrackPaint.getAlpha() > 0) {
|
canvas.translate(mRv.getWidth(), 0);
|
||||||
canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth,
|
|
||||||
mRv.getVisibleHeight(), mTrackPaint);
|
|
||||||
}
|
}
|
||||||
canvas.drawPath(mThumbPath, mThumbPaint);
|
// Draw the track
|
||||||
|
int thumbWidth = mIsRtl ? mWidth : -mWidth;
|
||||||
|
canvas.drawRect(0, 0, thumbWidth, mRv.getVisibleHeight(), mTrackPaint);
|
||||||
|
|
||||||
// Draw the popup
|
canvas.translate(0, mThumbOffsetY);
|
||||||
mPopup.draw(canvas);
|
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) {
|
private void showActiveScrollbar(boolean isScrolling) {
|
||||||
if (mScrollbarAnimator != null) {
|
if (mWidthAnimator != null) {
|
||||||
mScrollbarAnimator.cancel();
|
mWidthAnimator.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
mScrollbarAnimator = new AnimatorSet();
|
mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
|
||||||
ObjectAnimator trackWidthAnim = ObjectAnimator.ofInt(this, "trackWidth",
|
isScrolling ? mMaxWidth : mMinWidth);
|
||||||
isScrolling ? mThumbMaxWidth : mThumbMinWidth);
|
mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
|
||||||
ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "thumbWidth",
|
mWidthAnimator.start();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the specified points are near the scroll bar bounds.
|
* Returns whether the specified points are near the scroll bar bounds.
|
||||||
*/
|
*/
|
||||||
public boolean isNearThumb(int x, int y) {
|
public boolean isNearThumb(int x, int y) {
|
||||||
mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
|
int left = getDrawLeft();
|
||||||
mThumbOffset.y + mThumbHeight);
|
mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight);
|
||||||
mTmpRect.inset(mTouchInset, mTouchInset);
|
mTmpRect.inset(mTouchInset, mTouchInset);
|
||||||
return mTmpRect.contains(x, y);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
// 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.
|
// 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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -299,14 +299,14 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
|
|
||||||
// Skip early if there are no items or we haven't been measured
|
// Skip early if there are no items or we haven't been measured
|
||||||
if (items.isEmpty() || mNumAppsPerRow == 0) {
|
if (items.isEmpty() || mNumAppsPerRow == 0) {
|
||||||
mScrollbar.setThumbOffset(-1, -1);
|
mScrollbar.setThumbOffsetY(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip early if, there no child laid out in the container.
|
// Skip early if, there no child laid out in the container.
|
||||||
int scrollY = getCurrentScrollY();
|
int scrollY = getCurrentScrollY();
|
||||||
if (scrollY < 0) {
|
if (scrollY < 0) {
|
||||||
mScrollbar.setThumbOffset(-1, -1);
|
mScrollbar.setThumbOffsetY(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
int availableScrollBarHeight = getAvailableScrollBarHeight();
|
int availableScrollBarHeight = getAvailableScrollBarHeight();
|
||||||
int availableScrollHeight = getAvailableScrollHeight();
|
int availableScrollHeight = getAvailableScrollHeight();
|
||||||
if (availableScrollHeight <= 0) {
|
if (availableScrollHeight <= 0) {
|
||||||
mScrollbar.setThumbOffset(-1, -1);
|
mScrollbar.setThumbOffsetY(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,11 +323,10 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
// Calculate the current scroll position, the scrollY of the recycler view accounts
|
// 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
|
// for the view padding, while the scrollBarY is drawn right up to the background
|
||||||
// padding (ignoring padding)
|
// padding (ignoring padding)
|
||||||
int scrollBarX = getScrollBarX();
|
|
||||||
int scrollBarY = mBackgroundPadding.top +
|
int scrollBarY = mBackgroundPadding.top +
|
||||||
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
|
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
|
||||||
|
|
||||||
int thumbScrollY = mScrollbar.getThumbOffset().y;
|
int thumbScrollY = mScrollbar.getThumbOffsetY();
|
||||||
int diffScrollY = scrollBarY - thumbScrollY;
|
int diffScrollY = scrollBarY - thumbScrollY;
|
||||||
if (diffScrollY * dy > 0f) {
|
if (diffScrollY * dy > 0f) {
|
||||||
// User is scrolling in the same direction the thumb needs to catch up to the
|
// 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.min(offset, diffScrollY);
|
||||||
}
|
}
|
||||||
thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY));
|
thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY));
|
||||||
mScrollbar.setThumbOffset(scrollBarX, thumbScrollY);
|
mScrollbar.setThumbOffsetY(thumbScrollY);
|
||||||
if (scrollBarY == thumbScrollY) {
|
if (scrollBarY == thumbScrollY) {
|
||||||
mScrollbar.reattachThumbToScroll();
|
mScrollbar.reattachThumbToScroll();
|
||||||
}
|
}
|
||||||
|
@ -352,7 +351,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
// User is scrolling in an opposite direction to the direction that the thumb
|
// 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
|
// needs to catch up to the scroll position. Do nothing except for updating
|
||||||
// the scroll bar x to match the thumb width.
|
// the scroll bar x to match the thumb width.
|
||||||
mScrollbar.setThumbOffset(scrollBarX, thumbScrollY);
|
mScrollbar.setThumbOffsetY(thumbScrollY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -158,18 +158,13 @@ public class HolographicOutlineHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap createMediumDropShadow(BubbleTextView view) {
|
public Bitmap createMediumDropShadow(BubbleTextView view) {
|
||||||
return createMediumDropShadow(view.getIcon(), view.getScaleX(), view.getScaleY(), true);
|
Drawable drawable = view.getIcon();
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap createMediumDropShadow(Drawable drawable, boolean shouldCache) {
|
|
||||||
return createMediumDropShadow(drawable, 1f, 1f, shouldCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap createMediumDropShadow(Drawable drawable, float scaleX, float scaleY,
|
|
||||||
boolean shouldCache) {
|
|
||||||
if (drawable == null) {
|
if (drawable == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float scaleX = view.getScaleX();
|
||||||
|
float scaleY = view.getScaleY();
|
||||||
Rect rect = drawable.getBounds();
|
Rect rect = drawable.getBounds();
|
||||||
|
|
||||||
int bitmapWidth = (int) (rect.width() * scaleX);
|
int bitmapWidth = (int) (rect.width() * scaleX);
|
||||||
|
@ -179,14 +174,11 @@ public class HolographicOutlineHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
int key = (bitmapWidth << 16) | bitmapHeight;
|
int key = (bitmapWidth << 16) | bitmapHeight;
|
||||||
Bitmap cache = shouldCache ? mBitmapCache.get(key) : null;
|
Bitmap cache = mBitmapCache.get(key);
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8);
|
cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8);
|
||||||
mCanvas.setBitmap(cache);
|
mCanvas.setBitmap(cache);
|
||||||
|
mBitmapCache.put(key, cache);
|
||||||
if (shouldCache) {
|
|
||||||
mBitmapCache.put(key, cache);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
mCanvas.setBitmap(cache);
|
mCanvas.setBitmap(cache);
|
||||||
mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
|
mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
|
||||||
|
@ -206,7 +198,7 @@ public class HolographicOutlineHelper {
|
||||||
int resultWidth = bitmapWidth + extraSize;
|
int resultWidth = bitmapWidth + extraSize;
|
||||||
int resultHeight = bitmapHeight + extraSize;
|
int resultHeight = bitmapHeight + extraSize;
|
||||||
key = (resultWidth << 16) | resultHeight;
|
key = (resultWidth << 16) | resultHeight;
|
||||||
Bitmap result = shouldCache ? mBitmapCache.get(key) : null;
|
Bitmap result = mBitmapCache.get(key);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8);
|
result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8);
|
||||||
mCanvas.setBitmap(result);
|
mCanvas.setBitmap(result);
|
||||||
|
|
|
@ -121,7 +121,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
|
||||||
// Skip early if, there no child laid out in the container.
|
// Skip early if, there no child laid out in the container.
|
||||||
int scrollY = getCurrentScrollY();
|
int scrollY = getCurrentScrollY();
|
||||||
if (scrollY < 0) {
|
if (scrollY < 0) {
|
||||||
mScrollbar.setThumbOffset(-1, -1);
|
mScrollbar.setThumbOffsetY(-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue