diff options
5 files changed, 395 insertions, 307 deletions
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index b63ef788a..8d418f984 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -16,15 +16,28 @@ package com.android.launcher3; +import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + import com.android.launcher3.util.Thunk; /** - * A base {@link RecyclerView}, which will NOT intercept a touch sequence unless the scrolling - * velocity is below a predefined threshold. + * A base {@link RecyclerView}, which does the following: + * <ul> + * <li> NOT intercept a touch unless the scrolling velocity is below a predefined threshold. + * <li> Enable fast scroller. + * </ul> */ public class BaseRecyclerView extends RecyclerView implements RecyclerView.OnItemTouchListener { @@ -35,6 +48,53 @@ public class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; + // + // Keeps track of variables required for the second function of this class: fast scroller. + // + + private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; + + /** + * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() + * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so + * that we can calculate what the scroll bar looks like, and where to jump to from the fast + * scroller. + */ + public static class ScrollPositionState { + // The index of the first visible row + public int rowIndex; + // The offset of the first visible row + public int rowTopOffset; + // The height of a given row (they are currently all the same height) + public int rowHeight; + } + // Should be maintained inside overriden method #updateVerticalScrollbarBounds + public ScrollPositionState scrollPosState = new ScrollPositionState(); + public Rect verticalScrollbarBounds = new Rect(); + + private boolean mDraggingFastScroller; + + private Drawable mScrollbar; + private Drawable mFastScrollerBg; + private Rect mTmpFastScrollerInvalidateRect = new Rect(); + private Rect mFastScrollerBounds = new Rect(); + + private String mFastScrollSectionName; + private Paint mFastScrollTextPaint; + private Rect mFastScrollTextBounds = new Rect(); + private float mFastScrollAlpha; + + private int mDownX; + private int mDownY; + private int mLastX; + private int mLastY; + private int mScrollbarWidth; + private int mScrollbarMinHeight; + private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); + + + public BaseRecyclerView(Context context) { this(context, null); } @@ -49,6 +109,24 @@ public class BaseRecyclerView extends RecyclerView ScrollListener listener = new ScrollListener(); setOnScrollListener(listener); + + Resources res = context.getResources(); + int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); + mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); + mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); + mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); + mFastScrollTextPaint = new Paint(); + mFastScrollTextPaint.setColor(Color.WHITE); + mFastScrollTextPaint.setAntiAlias(true); + mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( + R.dimen.all_apps_fast_scroll_text_size)); + mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); + mScrollbarMinHeight = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); + mScrollbarInset = + res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); + setFastScrollerAlpha(mFastScrollAlpha); + setOverScrollMode(View.OVER_SCROLL_NEVER); } private class ScrollListener extends OnScrollListener { @@ -68,17 +146,74 @@ public class BaseRecyclerView extends RecyclerView addOnItemTouchListener(this); } + /** + * 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. + */ @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - if (shouldStopScroll(ev)) { - stopScroll(); - } - return false; + return handleTouchEvent(ev); } @Override public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - // Do nothing. + handleTouchEvent(ev); + } + + /** + * Handles the touch event and determines whether to show the fast scroller (or updates it if + * it is already showing). + */ + private boolean handleTouchEvent(MotionEvent ev) { + ViewConfiguration config = ViewConfiguration.get(getContext()); + + int action = ev.getAction(); + int x = (int) ev.getX(); + int y = (int) ev.getY(); + switch (action) { + case MotionEvent.ACTION_DOWN: + // Keep track of the down positions + mDownX = mLastX = x; + mDownY = mLastY = y; + if (shouldStopScroll(ev)) { + stopScroll(); + } + break; + case MotionEvent.ACTION_MOVE: + // Check if we are scrolling + if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && + Math.abs(y - mDownY) > config.getScaledTouchSlop()) { + getParent().requestDisallowInterceptTouchEvent(true); + mDraggingFastScroller = true; + animateFastScrollerVisibility(true); + } + if (mDraggingFastScroller) { + mLastX = x; + mLastY = y; + + // Scroll to the right position, and update the section name + int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); + int bottom = getHeight() - getPaddingBottom() - + (mFastScrollerBg.getBounds().height() / 2); + float boundedY = (float) Math.max(top, Math.min(bottom, y)); + mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / + (bottom - top)); + + // Combine the old and new fast scroller bounds to create the full invalidate + // rect + mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); + updateFastScrollerBounds(); + mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); + invalidateFastScroller(mTmpFastScrollerInvalidateRect); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mDraggingFastScroller = false; + animateFastScrollerVisibility(false); + break; + } + return mDraggingFastScroller; } public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -99,4 +234,127 @@ public class BaseRecyclerView extends RecyclerView } return false; } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + drawVerticalScrubber(canvas); + drawFastScrollerPopup(canvas); + } + + /** + * Draws the vertical scrollbar. + */ + private void drawVerticalScrubber(Canvas canvas) { + updateVerticalScrollbarBounds(); + + // Draw the scroll bar + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(verticalScrollbarBounds.left, verticalScrollbarBounds.top); + mScrollbar.setBounds(0, 0, mScrollbarWidth, verticalScrollbarBounds.height()); + mScrollbar.draw(canvas); + canvas.restoreToCount(restoreCount); + } + + /** + * Draws the fast scroller popup. + */ + private void drawFastScrollerPopup(Canvas canvas) { + if (mFastScrollAlpha > 0f && mFastScrollSectionName != null && !mFastScrollSectionName.isEmpty()) { + // Draw the fast scroller popup + int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); + mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollerBg.draw(canvas); + mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); + mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, + mFastScrollSectionName.length(), mFastScrollTextBounds); + float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); + canvas.drawText(mFastScrollSectionName, + (mFastScrollerBounds.width() - textWidth) / 2, + mFastScrollerBounds.height() - + (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, + mFastScrollTextPaint); + canvas.restoreToCount(restoreCount); + } + } + + /** + * Returns the scroll bar width. + */ + public int getScrollbarWidth() { + return mScrollbarWidth; + } + + /** + * Sets the fast scroller alpha. + */ + public void setFastScrollerAlpha(float alpha) { + mFastScrollAlpha = alpha; + invalidateFastScroller(mFastScrollerBounds); + } + + /** + * Maps the touch (from 0..1) to the adapter position that should be visible. + * <p>Override in each subclass of this base class. + */ + public String scrollToPositionAtProgress(float touchFraction) { + return null; + } + + /** + * Updates the bounds for the scrollbar. + * <p>Override in each subclass of this base class. + */ + public void updateVerticalScrollbarBounds() {}; + + /** + * Animates the visibility of the fast scroller popup. + */ + private void animateFastScrollerVisibility(boolean visible) { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); + anim.setDuration(visible ? 200 : 150); + anim.start(); + } + + /** + * Invalidates the fast scroller popup. + */ + protected void invalidateFastScroller(Rect bounds) { + invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); + } + + /** + * Returns whether a given point is near the scrollbar. + */ + private boolean isPointNearScrollbar(int x, int y) { + // Check if we are scrolling + updateVerticalScrollbarBounds(); + verticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); + return verticalScrollbarBounds.contains(x, y); + } + + /** + * Updates the bounds for the fast scroller. + */ + private void updateFastScrollerBounds() { + if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { + int x; + int y; + + // Calculate the position for the fast scroller popup + Rect bgBounds = mFastScrollerBg.getBounds(); + if (Utilities.isRtl(getResources())) { + x = mBackgroundPadding.left + getScrollBarSize(); + } else { + x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); + } + y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); + y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - + bgBounds.height())); + mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); + } else { + mFastScrollerBounds.setEmpty(); + } + } }
\ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index e95fa325a..cc5add3b2 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,75 +15,34 @@ */ package com.android.launcher3.allapps; -import android.animation.ObjectAnimator; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; + import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.R; import com.android.launcher3.Utilities; import java.util.List; /** - * A RecyclerView with custom fastscroll support. This is the main container for the all apps - * icons. + * A RecyclerView with custom fast scroll support for the all apps view. */ public class AllAppsRecyclerView extends BaseRecyclerView { - /** - * The current scroll state of the recycler view. We use this in updateVerticalScrollbarBounds() - * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so - * that we can calculate what the scroll bar looks like, and where to jump to from the fast - * scroller. - */ - private static class ScrollPositionState { - // The index of the first visible row - int rowIndex; - // The offset of the first visible row - int rowTopOffset; - // The height of a given row (they are currently all the same height) - int rowHeight; - } - - private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; - private AlphabeticalAppsList mApps; private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; - private Drawable mScrollbar; - private Drawable mFastScrollerBg; - private Rect mTmpFastScrollerInvalidateRect = new Rect(); - private Rect mFastScrollerBounds = new Rect(); - private Rect mVerticalScrollbarBounds = new Rect(); - private boolean mDraggingFastScroller; - private String mFastScrollSectionName; - private Paint mFastScrollTextPaint; - private Rect mFastScrollTextBounds = new Rect(); - private float mFastScrollAlpha; private int mPredictionBarHeight; - private int mDownX; - private int mDownY; - private int mLastX; - private int mLastY; - private int mScrollbarWidth; private int mScrollbarMinHeight; - private int mScrollbarInset; + private Rect mBackgroundPadding = new Rect(); - private ScrollPositionState mScrollPosState = new ScrollPositionState(); private Launcher mLauncher; @@ -102,25 +61,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView { public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); - mLauncher = (Launcher) context; - Resources res = context.getResources(); - int fastScrollerSize = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_popup_size); - mScrollbar = res.getDrawable(R.drawable.all_apps_scrollbar_thumb); - mFastScrollerBg = res.getDrawable(R.drawable.all_apps_fastscroll_bg); - mFastScrollerBg.setBounds(0, 0, fastScrollerSize, fastScrollerSize); - mFastScrollTextPaint = new Paint(); - mFastScrollTextPaint.setColor(Color.WHITE); - mFastScrollTextPaint.setAntiAlias(true); - mFastScrollTextPaint.setTextSize(res.getDimensionPixelSize( - R.dimen.all_apps_fast_scroll_text_size)); - mScrollbarWidth = res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_width); - mScrollbarMinHeight = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_bar_min_height); - mScrollbarInset = - res.getDimensionPixelSize(R.dimen.all_apps_fast_scroll_scrubber_touch_inset); - setFastScrollerAlpha(getFastScrollerAlpha()); - setOverScrollMode(View.OVER_SCROLL_NEVER); } /** @@ -158,28 +99,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView { } /** - * Sets the fast scroller alpha. - */ - public void setFastScrollerAlpha(float alpha) { - mFastScrollAlpha = alpha; - invalidateFastScroller(mFastScrollerBounds); - } - - /** - * Gets the fast scroller alpha. - */ - public float getFastScrollerAlpha() { - return mFastScrollAlpha; - } - - /** - * Returns the scroll bar width. - */ - public int getScrollbarWidth() { - return mScrollbarWidth; - } - - /** * Scrolls this recycler view to the top. */ public void scrollToTop() { @@ -191,11 +110,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView { */ public int getScrollPosition() { List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { + getCurScrollState(scrollPosState, items); + if (scrollPosState.rowIndex != -1) { int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; - return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + - predictionBarHeight - mScrollPosState.rowTopOffset; + return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) + + predictionBarHeight - scrollPosState.rowTopOffset; } return 0; } @@ -206,150 +125,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView { addOnItemTouchListener(this); } - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - drawVerticalScrubber(canvas); - drawFastScrollerPopup(canvas); - } - - /** - * 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. - */ - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) { - return handleTouchEvent(ev); - } - - @Override - public void onTouchEvent(RecyclerView rv, MotionEvent ev) { - handleTouchEvent(ev); - } - - /** - * Handles the touch event and determines whether to show the fast scroller (or updates it if - * it is already showing). - */ - private boolean handleTouchEvent(MotionEvent ev) { - ViewConfiguration config = ViewConfiguration.get(getContext()); - - int action = ev.getAction(); - int x = (int) ev.getX(); - int y = (int) ev.getY(); - switch (action) { - case MotionEvent.ACTION_DOWN: - // Keep track of the down positions - mDownX = mLastX = x; - mDownY = mLastY = y; - if (shouldStopScroll(ev)) { - stopScroll(); - } - break; - case MotionEvent.ACTION_MOVE: - // Check if we are scrolling - if (!mDraggingFastScroller && isPointNearScrollbar(mDownX, mDownY) && - Math.abs(y - mDownY) > config.getScaledTouchSlop()) { - getParent().requestDisallowInterceptTouchEvent(true); - mDraggingFastScroller = true; - animateFastScrollerVisibility(true); - } - if (mDraggingFastScroller) { - mLastX = x; - mLastY = y; - - // Scroll to the right position, and update the section name - int top = getPaddingTop() + (mFastScrollerBg.getBounds().height() / 2); - int bottom = getHeight() - getPaddingBottom() - - (mFastScrollerBg.getBounds().height() / 2); - float boundedY = (float) Math.max(top, Math.min(bottom, y)); - mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) / - (bottom - top)); - - // Combine the old and new fast scroller bounds to create the full invalidate - // rect - mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds); - updateFastScrollerBounds(); - mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds); - invalidateFastScroller(mTmpFastScrollerInvalidateRect); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mDraggingFastScroller = false; - animateFastScrollerVisibility(false); - break; - } - return mDraggingFastScroller; - } - - /** - * Animates the visibility of the fast scroller popup. - */ - private void animateFastScrollerVisibility(boolean visible) { - ObjectAnimator anim = ObjectAnimator.ofFloat(this, "fastScrollerAlpha", visible ? 1f : 0f); - anim.setDuration(visible ? 200 : 150); - anim.start(); - } - - /** - * Returns whether a given point is near the scrollbar. - */ - private boolean isPointNearScrollbar(int x, int y) { - // Check if we are scrolling - updateVerticalScrollbarBounds(); - mVerticalScrollbarBounds.inset(mScrollbarInset, mScrollbarInset); - return mVerticalScrollbarBounds.contains(x, y); - } - - /** - * Draws the fast scroller popup. - */ - private void drawFastScrollerPopup(Canvas canvas) { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - // Draw the fast scroller popup - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top); - mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollerBg.draw(canvas); - mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255)); - mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0, - mFastScrollSectionName.length(), mFastScrollTextBounds); - float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName); - canvas.drawText(mFastScrollSectionName, - (mFastScrollerBounds.width() - textWidth) / 2, - mFastScrollerBounds.height() - - (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2, - mFastScrollTextPaint); - canvas.restoreToCount(restoreCount); - } - } - - /** - * Draws the vertical scrollbar. - */ - private void drawVerticalScrubber(Canvas canvas) { - updateVerticalScrollbarBounds(); - - // Draw the scroll bar - int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.translate(mVerticalScrollbarBounds.left, mVerticalScrollbarBounds.top); - mScrollbar.setBounds(0, 0, mScrollbarWidth, mVerticalScrollbarBounds.height()); - mScrollbar.draw(canvas); - canvas.restoreToCount(restoreCount); - } - - /** - * Invalidates the fast scroller popup. - */ - private void invalidateFastScroller(Rect bounds) { - invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom); - } - /** * Maps the touch (from 0..1) to the adapter position that should be visible. */ - private String scrollToPositionAtProgress(float touchFraction) { + @Override + public String scrollToPositionAtProgress(float touchFraction) { // Ensure that we have any sections List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections = mApps.getFastScrollerSections(); @@ -393,27 +173,60 @@ public class AllAppsRecyclerView extends BaseRecyclerView { return lastScrollSection.sectionName; } + + /** + * Returns the row index for a app index in the list. + */ + private int findRowForAppIndex(int index) { + List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections(); + int appIndex = 0; + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + if (appIndex + info.numApps > index) { + return rowCount + ((index - appIndex) / mNumAppsPerRow); + } + appIndex += info.numApps; + rowCount += numRowsInSection; + } + return appIndex; + } + + /** + * Returns the total number of rows in the list. + */ + private int getNumRows() { + List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections(); + int rowCount = 0; + for (AlphabeticalAppsList.SectionInfo info : sections) { + int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); + rowCount += numRowsInSection; + } + return rowCount; + } + + /** * Updates the bounds for the scrollbar. */ - private void updateVerticalScrollbarBounds() { + @Override + public void updateVerticalScrollbarBounds() { List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); - // Skip early if there are no items + // Skip early if there are no items. if (items.isEmpty()) { - mVerticalScrollbarBounds.setEmpty(); + verticalScrollbarBounds.setEmpty(); return; } // Find the index and height of the first visible row (all rows have the same height) - int x; - int y; + int x, y; int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight; int rowCount = getNumRows(); - getCurScrollState(mScrollPosState, items); - if (mScrollPosState.rowIndex != -1) { + getCurScrollState(scrollPosState, items); + if (scrollPosState.rowIndex != -1) { int height = getHeight() - getPaddingTop() - getPaddingBottom(); - int totalScrollHeight = rowCount * mScrollPosState.rowHeight + predictionBarHeight; + int totalScrollHeight = rowCount * scrollPosState.rowHeight + predictionBarHeight; if (totalScrollHeight > height) { int scrollbarHeight = Math.max(mScrollbarMinHeight, (int) (height / ((float) totalScrollHeight / height))); @@ -422,78 +235,23 @@ public class AllAppsRecyclerView extends BaseRecyclerView { if (Utilities.isRtl(getResources())) { x = mBackgroundPadding.left; } else { - x = getWidth() - mBackgroundPadding.right - mScrollbarWidth; + x = getWidth() - mBackgroundPadding.right - getScrollbarWidth(); } // To calculate the offset, we compute the percentage of the total scrollable height // that the user has already scrolled and then map that to the scroll bar bounds int availableY = totalScrollHeight - height; int availableScrollY = height - scrollbarHeight; - y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + predictionBarHeight - - mScrollPosState.rowTopOffset; + y = (scrollPosState.rowIndex * scrollPosState.rowHeight) + predictionBarHeight + - scrollPosState.rowTopOffset; y = getPaddingTop() + (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY); - mVerticalScrollbarBounds.set(x, y, x + mScrollbarWidth, y + scrollbarHeight); + verticalScrollbarBounds.set(x, y, x + getScrollbarWidth(), y + scrollbarHeight); return; } } - mVerticalScrollbarBounds.setEmpty(); - } - - /** - * Updates the bounds for the fast scroller. - */ - private void updateFastScrollerBounds() { - if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) { - int x; - int y; - - // Calculate the position for the fast scroller popup - Rect bgBounds = mFastScrollerBg.getBounds(); - if (Utilities.isRtl(getResources())) { - x = mBackgroundPadding.left + getScrollBarSize(); - } else { - x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width(); - } - y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height()); - y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() - - bgBounds.height())); - mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height()); - } else { - mFastScrollerBounds.setEmpty(); - } - } - - /** - * Returns the row index for a app index in the list. - */ - private int findRowForAppIndex(int index) { - List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections(); - int appIndex = 0; - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - if (appIndex + info.numApps > index) { - return rowCount + ((index - appIndex) / mNumAppsPerRow); - } - appIndex += info.numApps; - rowCount += numRowsInSection; - } - return appIndex; - } - - /** - * Returns the total number of rows in the list. - */ - private int getNumRows() { - List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections(); - int rowCount = 0; - for (AlphabeticalAppsList.SectionInfo info : sections) { - int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow); - rowCount += numRowsInSection; - } - return rowCount; + verticalScrollbarBounds.setEmpty(); } /** diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 778cf9ef1..11c2107f2 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -65,7 +65,7 @@ public class WidgetsContainerView extends BaseContainerView private IconCache mIconCache; /* Recycler view related member variables */ - private RecyclerView mView; + private WidgetsRecyclerView mView; private WidgetsListAdapter mAdapter; /* Touch handling related member variables. */ @@ -100,7 +100,7 @@ public class WidgetsContainerView extends BaseContainerView @Override protected void onFinishInflate() { - mView = (RecyclerView) findViewById(R.id.widgets_list_view); + mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view); mView.setAdapter(mAdapter); // This extends the layout space so that preloading happen for the {@link RecyclerView} @@ -351,6 +351,7 @@ public class WidgetsContainerView extends BaseContainerView * Initialize the widget data model. */ public void addWidgets(WidgetsModel model) { + mView.setWidgets(model); mAdapter.setWidgetsModel(model); mAdapter.notifyDataSetChanged(); } diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index 7439a44f8..e82c0a631 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -32,7 +32,6 @@ import android.widget.LinearLayout; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.IconCache; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 31ef5d6fc..bef255908 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -17,14 +17,23 @@ package com.android.launcher3.widget; import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; +import android.view.MotionEvent; + import com.android.launcher3.BaseRecyclerView; +import com.android.launcher3.model.WidgetsModel; /** * The widgets recycler view. */ public class WidgetsRecyclerView extends BaseRecyclerView { + private WidgetsModel mWidgets; + private Rect mBackgroundPadding = new Rect(); + public WidgetsRecyclerView(Context context) { this(context, null); } @@ -37,4 +46,67 @@ public class WidgetsRecyclerView extends BaseRecyclerView { super(context, attrs, defStyleAttr); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + addOnItemTouchListener(this); + } + + public void updateBackgroundPadding(Drawable background) { + background.getPadding(mBackgroundPadding); + } + + /** + * Sets the widget model in this view, used to determine the fast scroll position. + */ + public void setWidgets(WidgetsModel widgets) { + mWidgets = widgets; + } + + /** + * Maps the touch (from 0..1) to the adapter position that should be visible. + */ + @Override + public String scrollToPositionAtProgress(float touchFraction) { + // Ensure that we have any sections + return ""; + } + + /** + * Updates the bounds for the scrollbar. + */ + @Override + public void updateVerticalScrollbarBounds() { + int rowCount = mWidgets.getPackageSize(); + + // Skip early if there are no items. + if (rowCount == 0) { + verticalScrollbarBounds.setEmpty(); + return; + } + + int x, y; + getCurScrollState(scrollPosState); + if (scrollPosState.rowIndex < 0) { + verticalScrollbarBounds.setEmpty(); + } + // TODO + } + + /** + * Returns the current scroll state. + */ + private void getCurScrollState(ScrollPositionState stateOut) { + stateOut.rowIndex = -1; + stateOut.rowTopOffset = -1; + stateOut.rowHeight = -1; + + int rowCount = mWidgets.getPackageSize(); + + // Return early if there are no items + if (rowCount == 0) { + return; + } + // TODO + } }
\ No newline at end of file |