From ca0c07d45ba8735b733c4deaa21404777468534b Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Thu, 30 Oct 2014 10:26:23 +0000 Subject: [PATCH] Bug 1090364 - Add fading edge support to TwoWayView (r=mcomella) --- mobile/android/base/widget/TwoWayView.java | 200 ++++++++++++++++++--- 1 file changed, 178 insertions(+), 22 deletions(-) diff --git a/mobile/android/base/widget/TwoWayView.java b/mobile/android/base/widget/TwoWayView.java index 255290142c6e..79cc4f6125ff 100644 --- a/mobile/android/base/widget/TwoWayView.java +++ b/mobile/android/base/widget/TwoWayView.java @@ -1112,6 +1112,110 @@ public class TwoWayView extends AdapterView implements return INVALID_POSITION; } + @Override + protected float getTopFadingEdgeStrength() { + if (!mIsVertical) { + return 0f; + } + + final float fadingEdge = super.getTopFadingEdgeStrength(); + + final int childCount = getChildCount(); + if (childCount == 0) { + return fadingEdge; + } else { + if (mFirstPosition > 0) { + return 1.0f; + } + + final int top = getChildAt(0).getTop(); + final int paddingTop = getPaddingTop(); + + final float length = (float) getVerticalFadingEdgeLength(); + + return (top < paddingTop ? (float) -(top - paddingTop) / length : fadingEdge); + } + } + + @Override + protected float getBottomFadingEdgeStrength() { + if (!mIsVertical) { + return 0f; + } + + final float fadingEdge = super.getBottomFadingEdgeStrength(); + + final int childCount = getChildCount(); + if (childCount == 0) { + return fadingEdge; + } else { + if (mFirstPosition + childCount - 1 < mItemCount - 1) { + return 1.0f; + } + + final int bottom = getChildAt(childCount - 1).getBottom(); + final int paddingBottom = getPaddingBottom(); + + final int height = getHeight(); + final float length = (float) getVerticalFadingEdgeLength(); + + return (bottom > height - paddingBottom ? + (float) (bottom - height + paddingBottom) / length : fadingEdge); + } + } + + @Override + protected float getLeftFadingEdgeStrength() { + if (mIsVertical) { + return 0f; + } + + final float fadingEdge = super.getLeftFadingEdgeStrength(); + + final int childCount = getChildCount(); + if (childCount == 0) { + return fadingEdge; + } else { + if (mFirstPosition > 0) { + return 1.0f; + } + + final int left = getChildAt(0).getLeft(); + final int paddingLeft = getPaddingLeft(); + + final float length = (float) getHorizontalFadingEdgeLength(); + + return (left < paddingLeft ? (float) -(left - paddingLeft) / length : fadingEdge); + } + } + + @Override + protected float getRightFadingEdgeStrength() { + if (mIsVertical) { + return 0f; + } + + final float fadingEdge = super.getRightFadingEdgeStrength(); + + final int childCount = getChildCount(); + if (childCount == 0) { + return fadingEdge; + } else { + if (mFirstPosition + childCount - 1 < mItemCount - 1) { + return 1.0f; + } + + final int right = getChildAt(childCount - 1).getRight(); + final int paddingRight = getPaddingRight(); + + final int width = getWidth(); + final float length = (float) getHorizontalFadingEdgeLength(); + + return (right > width - paddingRight ? + (float) (right - width + paddingRight) / length : fadingEdge); + } + } + @Override protected int computeVerticalScrollExtent() { final int count = getChildCount(); @@ -1814,7 +1918,7 @@ public class TwoWayView extends AdapterView implements final int position = lookForSelectablePosition(nextPage, forward); if (position >= 0) { mLayoutMode = LAYOUT_SPECIFIC; - mSpecificStart = (mIsVertical ? getPaddingTop() : getPaddingLeft()); + mSpecificStart = getStartEdge() + getFadingEdgeLength(); if (forward && position > mItemCount - getChildCount()) { mLayoutMode = LAYOUT_FORCE_BOTTOM; @@ -2073,7 +2177,9 @@ public class TwoWayView extends AdapterView implements newFocus = FocusFinder.getInstance().findNextFocus(this, oldFocus, direction); } else { if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) { - final int start = getStartEdge(); + boolean fadingEdgeShowing = (mFirstPosition > 0); + final int start = getStartEdge() + + (fadingEdgeShowing ? getArrowScrollPreviewLength() : 0); final int selectedStart; if (selectedView != null) { @@ -2084,7 +2190,9 @@ public class TwoWayView extends AdapterView implements searchPoint = Math.max(selectedStart, start); } else { - final int end = getEndEdge(); + final boolean fadingEdgeShowing = + (mFirstPosition + getChildCount() - 1) < mItemCount; + final int end = getEndEdge() - (fadingEdgeShowing ? getArrowScrollPreviewLength() : 0); final int selectedEnd; if (selectedView != null) { @@ -2157,12 +2265,7 @@ public class TwoWayView extends AdapterView implements * @return The amount to preview next items when arrow scrolling. */ private int getArrowScrollPreviewLength() { - // FIXME: TwoWayView has no fading edge support just yet but using it - // makes it convenient for defining the next item's previous length. - int fadingEdgeLength = - (mIsVertical ? getVerticalFadingEdgeLength() : getHorizontalFadingEdgeLength()); - - return mItemMargin + Math.max(MIN_SCROLL_PREVIEW_PIXELS, fadingEdgeLength); + return mItemMargin + Math.max(MIN_SCROLL_PREVIEW_PIXELS, getFadingEdgeLength()); } /** @@ -2958,6 +3061,30 @@ public class TwoWayView extends AdapterView implements return (mIsVertical ? child.getMeasuredHeight() : child.getMeasuredWidth()); } + private int getFadingEdgeLength() { + return (mIsVertical ? getVerticalFadingEdgeLength() : getHorizontalFadingEdgeLength()); + } + + private int getMinSelectionPixel(int start, int fadingEdgeLength, int selectedPosition) { + // First pixel we can draw the selection into. + int selectionPixelStart = start; + if (selectedPosition > 0) { + selectionPixelStart += fadingEdgeLength; + } + + return selectionPixelStart; + } + + private int getMaxSelectionPixel(int end, int fadingEdgeLength, + int selectedPosition) { + int selectionPixelEnd = end; + if (selectedPosition != mItemCount - 1) { + selectionPixelEnd -= fadingEdgeLength; + } + + return selectionPixelEnd; + } + private boolean contentFits() { final int childCount = getChildCount(); if (childCount == 0) { @@ -4191,11 +4318,15 @@ public class TwoWayView extends AdapterView implements private View moveSelection(View oldSelected, View newSelected, int delta, int start, int end) { + final int fadingEdgeLength = getFadingEdgeLength(); final int selectedPosition = mSelectedPosition; final int oldSelectedStart = getChildStartEdge(oldSelected); final int oldSelectedEnd = getChildEndEdge(oldSelected); + final int minStart = getMinSelectionPixel(start, fadingEdgeLength, selectedPosition); + final int maxEnd = getMaxSelectionPixel(end, fadingEdgeLength, selectedPosition); + View selected = null; if (delta > 0) { @@ -4233,10 +4364,10 @@ public class TwoWayView extends AdapterView implements // Some of the newly selected item extends below the bottom of the list if (selectedEnd > end) { // Find space available above the selection into which we can scroll upwards - final int spaceBefore = selectedStart - start; + final int spaceBefore = selectedStart - minStart; // Find space required to bring the bottom of the selected item fully into view - final int spaceAfter = selectedEnd - end; + final int spaceAfter = selectedEnd - maxEnd; // Don't scroll more than half the size of the list final int halfSpace = (end - start) / 2; @@ -4291,12 +4422,12 @@ public class TwoWayView extends AdapterView implements final int selectedEnd = getChildEndEdge(selected); // Some of the newly selected item extends above the top of the list - if (selectedStart < start) { + if (selectedStart < minStart) { // Find space required to bring the top of the selected item fully into view - final int spaceBefore = start - selectedStart; + final int spaceBefore = minStart - selectedStart; // Find space available below the selection into which we can scroll downwards - final int spaceAfter = end - selectedEnd; + final int spaceAfter = maxEnd - selectedEnd; // Don't scroll more than half the height of the list final int halfSpace = (end - start) / 2; @@ -4515,8 +4646,8 @@ public class TwoWayView extends AdapterView implements int selectedStart = 0; int selectedPosition; - final int start = getStartEdge(); - final int end = getEndEdge(); + int start = getStartEdge(); + int end = getEndEdge(); final int firstPosition = mFirstPosition; final int toPosition = mResurrectToPosition; @@ -4527,6 +4658,15 @@ public class TwoWayView extends AdapterView implements final View selected = getChildAt(selectedPosition - mFirstPosition); selectedStart = getChildStartEdge(selected); + + final int selectedEnd = getChildEndEdge(selected); + + // We are scrolled, don't get in the fade + if (selectedStart < start) { + selectedStart = start + getFadingEdgeLength(); + } else if (selectedEnd > end) { + selectedStart = end - getChildMeasuredSize(selected) - getFadingEdgeLength(); + } } else if (toPosition < firstPosition) { // Default to selecting whatever is first selectedPosition = firstPosition; @@ -4538,6 +4678,13 @@ public class TwoWayView extends AdapterView implements if (i == 0) { // Remember the position of the first item selectedStart = childStart; + + // See if we are scrolled at all + if (firstPosition > 0 || childStart < start) { + // If we are scrolled, don't select anything that is + // in the fade region + start += getFadingEdgeLength(); + } } if (childStart >= start) { @@ -4548,6 +4695,7 @@ public class TwoWayView extends AdapterView implements } } } else { + final int itemCount = mItemCount; selectedPosition = firstPosition + childCount - 1; down = false; @@ -4558,6 +4706,10 @@ public class TwoWayView extends AdapterView implements if (i == childCount - 1) { selectedStart = childStart; + + if (firstPosition + childCount < itemCount || childEnd > end) { + end -= getFadingEdgeLength(); + } } if (childEnd <= end) { @@ -5093,35 +5245,39 @@ public class TwoWayView extends AdapterView implements } private View fillFromSelection(int selectedTop, int start, int end) { + int fadingEdgeLength = getFadingEdgeLength(); final int selectedPosition = mSelectedPosition; + final int minStart = getMinSelectionPixel(start, fadingEdgeLength, selectedPosition); + final int maxEnd = getMaxSelectionPixel(end, fadingEdgeLength, selectedPosition); + View selected = makeAndAddView(selectedPosition, selectedTop, true, true); final int selectedStart = getChildStartEdge(selected); final int selectedEnd = getChildEndEdge(selected); // Some of the newly selected item extends below the bottom of the list - if (selectedEnd > end) { + if (selectedEnd > maxEnd) { // Find space available above the selection into which we can scroll // upwards - final int spaceAbove = selectedStart - start; + final int spaceAbove = selectedStart - minStart; // Find space required to bring the bottom of the selected item // fully into view - final int spaceBelow = selectedEnd - end; + final int spaceBelow = selectedEnd - maxEnd; final int offset = Math.min(spaceAbove, spaceBelow); // Now offset the selected item to get it into view selected.offsetTopAndBottom(-offset); - } else if (selectedStart < start) { + } else if (selectedStart < minStart) { // Find space required to bring the top of the selected item fully // into view - final int spaceAbove = start - selectedStart; + final int spaceAbove = minStart - selectedStart; // Find space available below the selection into which we can scroll // downwards - final int spaceBelow = end - selectedEnd; + final int spaceBelow = maxEnd - selectedEnd; final int offset = Math.min(spaceAbove, spaceBelow);