diff options
| -rw-r--r-- | core/java/android/view/View.java | 318 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 21 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 7 | ||||
| -rw-r--r-- | core/java/android/widget/HorizontalScrollView.java | 4 | ||||
| -rw-r--r-- | core/java/android/widget/ScrollBarDrawable.java | 24 | ||||
| -rw-r--r-- | core/java/android/widget/ScrollView.java | 4 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/ScrollBarUtils.java | 39 |
7 files changed, 342 insertions, 75 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 537f887305ee..eddcd25c4184 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -98,13 +98,13 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Checkable; import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; - import static android.os.Build.VERSION_CODES.*; import static java.lang.Math.max; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.widget.ScrollBarUtils; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -5110,6 +5110,88 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mVerticalScrollbarPosition; } + boolean isOnScrollbar(float x, float y) { + if (mScrollCache == null) { + return false; + } + x += getScrollX(); + y += getScrollY(); + if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + if (bounds.contains((int)x, (int)y)) { + return true; + } + } + if (isHorizontalScrollBarEnabled()) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + if (bounds.contains((int)x, (int)y)) { + return true; + } + } + return false; + } + + boolean isOnScrollbarThumb(float x, float y) { + return isOnVerticalScrollbarThumb(x, y) || isOnHorizontalScrollbarThumb(x, y); + } + + private boolean isOnVerticalScrollbarThumb(float x, float y) { + if (mScrollCache == null) { + return false; + } + if (isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden()) { + x += getScrollX(); + y += getScrollY(); + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + final int range = computeVerticalScrollRange(); + final int offset = computeVerticalScrollOffset(); + final int extent = computeVerticalScrollExtent(); + final int thumbLength = ScrollBarUtils.getThumbLength(bounds.height(), bounds.width(), + extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.height(), thumbLength, + extent, range, offset); + final int thumbTop = bounds.top + thumbOffset; + if (x >= bounds.left && x <= bounds.right && y >= thumbTop + && y <= thumbTop + thumbLength) { + return true; + } + } + return false; + } + + private boolean isOnHorizontalScrollbarThumb(float x, float y) { + if (mScrollCache == null) { + return false; + } + if (isHorizontalScrollBarEnabled()) { + x += getScrollX(); + y += getScrollY(); + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + final int range = computeHorizontalScrollRange(); + final int offset = computeHorizontalScrollOffset(); + final int extent = computeHorizontalScrollExtent(); + final int thumbLength = ScrollBarUtils.getThumbLength(bounds.width(), bounds.height(), + extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset(bounds.width(), thumbLength, + extent, range, offset); + final int thumbLeft = bounds.left + thumbOffset; + if (x >= thumbLeft && x <= thumbLeft + thumbLength && y >= bounds.top + && y <= bounds.bottom) { + return true; + } + } + return false; + } + + boolean isDraggingScrollBar() { + return mScrollCache != null + && mScrollCache.mScrollBarDraggingState != ScrollabilityCache.NOT_DRAGGING; + } + /** * Sets the state of all scroll indicators. * <p> @@ -9586,6 +9668,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } if (onFilterTouchEventForSecurity(event)) { + if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { + result = true; + } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null @@ -10450,6 +10535,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + if ((action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_MOVE) + && event.isFromSource(InputDevice.SOURCE_MOUSE) + && isOnScrollbar(event.getX(), event.getY())) { + awakenScrollBars(); + } if (isHoverable()) { switch (action) { case MotionEvent.ACTION_HOVER_ENTER: @@ -10553,6 +10643,110 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Handles scroll bar dragging by mouse input. + * + * @hide + * @param event The motion event. + * + * @return true if the event was handled as a scroll bar dragging, false otherwise. + */ + protected boolean handleScrollBarDragging(MotionEvent event) { + if (mScrollCache == null) { + return false; + } + final float x = event.getX(); + final float y = event.getY(); + final int action = event.getAction(); + if ((mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING + && action != MotionEvent.ACTION_DOWN) + || !event.isFromSource(InputDevice.SOURCE_MOUSE) + || !event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) { + mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING; + return false; + } + + switch (action) { + case MotionEvent.ACTION_MOVE: + if (mScrollCache.mScrollBarDraggingState == ScrollabilityCache.NOT_DRAGGING) { + return false; + } + if (mScrollCache.mScrollBarDraggingState + == ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + final int range = computeVerticalScrollRange(); + final int offset = computeVerticalScrollOffset(); + final int extent = computeVerticalScrollExtent(); + + final int thumbLength = ScrollBarUtils.getThumbLength( + bounds.height(), bounds.width(), extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset( + bounds.height(), thumbLength, extent, range, offset); + + final float diff = y - mScrollCache.mScrollBarDraggingPos; + final float maxThumbOffset = bounds.height() - thumbLength; + final float newThumbOffset = + Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset); + final int height = getHeight(); + if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0 + && height > 0 && extent > 0) { + final int newY = Math.round((range - extent) + / ((float)extent / height) * (newThumbOffset / maxThumbOffset)); + if (newY != getScrollY()) { + mScrollCache.mScrollBarDraggingPos = y; + setScrollY(newY); + } + } + return true; + } + if (mScrollCache.mScrollBarDraggingState + == ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR) { + final Rect bounds = mScrollCache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + final int range = computeHorizontalScrollRange(); + final int offset = computeHorizontalScrollOffset(); + final int extent = computeHorizontalScrollExtent(); + + final int thumbLength = ScrollBarUtils.getThumbLength( + bounds.width(), bounds.height(), extent, range); + final int thumbOffset = ScrollBarUtils.getThumbOffset( + bounds.width(), thumbLength, extent, range, offset); + + final float diff = x - mScrollCache.mScrollBarDraggingPos; + final float maxThumbOffset = bounds.width() - thumbLength; + final float newThumbOffset = + Math.min(Math.max(thumbOffset + diff, 0.0f), maxThumbOffset); + final int width = getWidth(); + if (Math.round(newThumbOffset) != thumbOffset && maxThumbOffset > 0 + && width > 0 && extent > 0) { + final int newX = Math.round((range - extent) + / ((float)extent / width) * (newThumbOffset / maxThumbOffset)); + if (newX != getScrollX()) { + mScrollCache.mScrollBarDraggingPos = x; + setScrollX(newX); + } + } + return true; + } + case MotionEvent.ACTION_DOWN: + if (isOnVerticalScrollbarThumb(x, y)) { + mScrollCache.mScrollBarDraggingState = + ScrollabilityCache.DRAGGING_VERTICAL_SCROLL_BAR; + mScrollCache.mScrollBarDraggingPos = y; + return true; + } + if (isOnHorizontalScrollbarThumb(x, y)) { + mScrollCache.mScrollBarDraggingState = + ScrollabilityCache.DRAGGING_HORIZONTAL_SCROLL_BAR; + mScrollCache.mScrollBarDraggingPos = x; + return true; + } + } + mScrollCache.mScrollBarDraggingState = ScrollabilityCache.NOT_DRAGGING; + return false; + } + + /** * Implement this method to handle touch screen motion events. * <p> * If this method is used to detect click actions, it is recommended that @@ -10585,7 +10779,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE); } - if (mTouchDelegate != null) { if (mTouchDelegate.onTouchEvent(event)) { return true; @@ -14146,6 +14339,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + private void getHorizontalScrollBarBounds(Rect bounds) { + final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; + final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() + && !isVerticalScrollBarHidden(); + final int size = getHorizontalScrollbarHeight(); + final int verticalScrollBarGap = drawVerticalScrollBar ? + getVerticalScrollbarWidth() : 0; + final int width = mRight - mLeft; + final int height = mBottom - mTop; + bounds.top = mScrollY + height - size - (mUserPaddingBottom & inside); + bounds.left = mScrollX + (mPaddingLeft & inside); + bounds.right = mScrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; + bounds.bottom = bounds.top + size; + } + + private void getVerticalScrollBarBounds(Rect bounds) { + final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; + final int size = getVerticalScrollbarWidth(); + int verticalScrollbarPosition = mVerticalScrollbarPosition; + if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) { + verticalScrollbarPosition = isLayoutRtl() ? + SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT; + } + final int width = mRight - mLeft; + final int height = mBottom - mTop; + switch (verticalScrollbarPosition) { + default: + case SCROLLBAR_POSITION_RIGHT: + bounds.left = mScrollX + width - size - (mUserPaddingRight & inside); + break; + case SCROLLBAR_POSITION_LEFT: + bounds.left = mScrollX + (mUserPaddingLeft & inside); + break; + } + bounds.top = mScrollY + (mPaddingTop & inside); + bounds.right = bounds.left + size; + bounds.bottom = mScrollY + height - (mUserPaddingBottom & inside); + } + /** * <p>Request the drawing of the horizontal and the vertical scrollbar. The * scrollbars are painted only if they have been awakened first.</p> @@ -14193,80 +14425,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, cache.scrollBar.mutate().setAlpha(255); } - - final int viewFlags = mViewFlags; - - final boolean drawHorizontalScrollBar = - (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL; - final boolean drawVerticalScrollBar = - (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL - && !isVerticalScrollBarHidden(); + final boolean drawHorizontalScrollBar = isHorizontalScrollBarEnabled(); + final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() + && !isVerticalScrollBarHidden(); if (drawVerticalScrollBar || drawHorizontalScrollBar) { - final int width = mRight - mLeft; - final int height = mBottom - mTop; - final ScrollBarDrawable scrollBar = cache.scrollBar; - final int scrollX = mScrollX; - final int scrollY = mScrollY; - final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0; - - int left; - int top; - int right; - int bottom; - if (drawHorizontalScrollBar) { - int size = scrollBar.getSize(false); - if (size <= 0) { - size = cache.scrollBarSize; - } - scrollBar.setParameters(computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); - final int verticalScrollBarGap = drawVerticalScrollBar ? - getVerticalScrollbarWidth() : 0; - top = scrollY + height - size - (mUserPaddingBottom & inside); - left = scrollX + (mPaddingLeft & inside); - right = scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap; - bottom = top + size; - onDrawHorizontalScrollBar(canvas, scrollBar, left, top, right, bottom); + final Rect bounds = cache.mScrollBarBounds; + getHorizontalScrollBarBounds(bounds); + onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top, + bounds.right, bounds.bottom); if (invalidate) { - invalidate(left, top, right, bottom); + invalidate(bounds); } } if (drawVerticalScrollBar) { - int size = scrollBar.getSize(true); - if (size <= 0) { - size = cache.scrollBarSize; - } - scrollBar.setParameters(computeVerticalScrollRange(), computeVerticalScrollOffset(), computeVerticalScrollExtent(), true); - int verticalScrollbarPosition = mVerticalScrollbarPosition; - if (verticalScrollbarPosition == SCROLLBAR_POSITION_DEFAULT) { - verticalScrollbarPosition = isLayoutRtl() ? - SCROLLBAR_POSITION_LEFT : SCROLLBAR_POSITION_RIGHT; - } - switch (verticalScrollbarPosition) { - default: - case SCROLLBAR_POSITION_RIGHT: - left = scrollX + width - size - (mUserPaddingRight & inside); - break; - case SCROLLBAR_POSITION_LEFT: - left = scrollX + (mUserPaddingLeft & inside); - break; - } - top = scrollY + (mPaddingTop & inside); - right = left + size; - bottom = scrollY + height - (mUserPaddingBottom & inside); - onDrawVerticalScrollBar(canvas, scrollBar, left, top, right, bottom); + final Rect bounds = cache.mScrollBarBounds; + getVerticalScrollBarBounds(bounds); + onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top, + bounds.right, bounds.bottom); if (invalidate) { - invalidate(left, top, right, bottom); + invalidate(bounds); } } } @@ -21201,6 +21389,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @see PointerIcon */ public PointerIcon getPointerIcon(MotionEvent event, float x, float y) { + if (isDraggingScrollBar() || isOnScrollbarThumb(x, y)) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW); + } return mPointerIcon; } @@ -22457,6 +22648,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int mLastColor; + public final Rect mScrollBarBounds = new Rect(); + + public static final int NOT_DRAGGING = 0; + public static final int DRAGGING_VERTICAL_SCROLL_BAR = 1; + public static final int DRAGGING_HORIZONTAL_SCROLL_BAR = 2; + public int mScrollBarDraggingState = NOT_DRAGGING; + + public float mScrollBarDraggingPos = 0; + public ScrollabilityCache(ViewConfiguration configuration, View host) { fadingEdgeLength = configuration.getScaledFadingEdgeLength(); scrollBarSize = configuration.getScaledScrollBarSize(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1c243929fa49..5d02c175794b 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -59,6 +59,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; + import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; /** @@ -1717,6 +1718,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public PointerIcon getPointerIcon(MotionEvent event, float x, float y) { + if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.STYLE_ARROW); + } // Check what the child under the pointer says about the pointer. final int childrenCount = mChildrenCount; if (childrenCount != 0) { @@ -2027,7 +2031,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * hover exit event in {@link #onHoverEvent} and then the hovered child will * receive a hover enter event. * </p><p> - * The default implementation always returns false. + * The default implementation handles mouse hover on the scroll bars. * </p> * * @param event The motion event that describes the hover. @@ -2035,6 +2039,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * and prevent its children from receiving it. */ public boolean onInterceptHoverEvent(MotionEvent event) { + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + final int action = event.getAction(); + final float x = event.getX(); + final float y = event.getY(); + if ((action == MotionEvent.ACTION_HOVER_MOVE + || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) { + return true; + } + } return false; } @@ -2781,6 +2794,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * messages will be delivered here. */ public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.isFromSource(InputDevice.SOURCE_MOUSE) + && ev.getAction() == MotionEvent.ACTION_DOWN + && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY) + && isOnScrollbarThumb(ev.getX(), ev.getY())) { + return true; + } return false; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e1ce9fe7c039..bf3dce037e91 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3669,6 +3669,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** @hide */ + @Override + protected boolean handleScrollBarDragging(MotionEvent event) { + // Doesn't support normal scroll bar dragging. Use FastScroller. + return false; + } + @Override public boolean onTouchEvent(MotionEvent ev) { if (!isEnabled()) { diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index ebc7eb3e3646..f16fdd657f84 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -454,6 +454,10 @@ public class HorizontalScrollView extends FrameLayout { return true; } + if (super.onInterceptTouchEvent(ev)) { + return true; + } + switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_MOVE: { /* diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index 91d623216137..8880217d9461 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -16,6 +16,8 @@ package android.widget; +import com.android.internal.widget.ScrollBarUtils; + import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; @@ -135,23 +137,15 @@ public class ScrollBarDrawable extends Drawable implements Drawable.Callback { } if (drawThumb) { - final int size = vertical ? r.height() : r.width(); + final int scrollBarLength = vertical ? r.height() : r.width(); final int thickness = vertical ? r.width() : r.height(); - final int minLength = thickness * 2; - - // Avoid the tiny thumb. - int length = Math.round((float) size * extent / range); - if (length < minLength) { - length = minLength; - } - - // Avoid the too-big thumb. - int offset = Math.round((float) (size - length) * mOffset / (range - extent)); - if (offset > size - length) { - offset = size - length; - } + final int thumbLength = + ScrollBarUtils.getThumbLength(scrollBarLength, thickness, extent, range); + final int thumbOffset = + ScrollBarUtils.getThumbOffset(scrollBarLength, thumbLength, extent, range, + mOffset); - drawThumb(canvas, r, offset, length, vertical); + drawThumb(canvas, r, thumbOffset, thumbLength, vertical); } } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 78b931d94fd2..3f7a07bbd31a 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -489,6 +489,10 @@ public class ScrollView extends FrameLayout { return true; } + if (super.onInterceptTouchEvent(ev)) { + return true; + } + /* * Don't try to intercept touch if we can't scroll anyway. */ diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java new file mode 100644 index 000000000000..0ae9f74167d5 --- /dev/null +++ b/core/java/com/android/internal/widget/ScrollBarUtils.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 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.internal.widget; + +public class ScrollBarUtils { + + public static int getThumbLength(int size, int thickness, int extent, int range) { + // Avoid the tiny thumb. + final int minLength = thickness * 2; + int length = Math.round((float) size * extent / range); + if (length < minLength) { + length = minLength; + } + return length; + } + + public static int getThumbOffset(int size, int thumbLength, int extent, int range, int offset) { + // Avoid the too-big thumb. + int thumbOffset = Math.round((float) (size - thumbLength) * offset / (range - extent)); + if (thumbOffset > size - thumbLength) { + thumbOffset = size - thumbLength; + } + return thumbOffset; + } +} |