FileDocCategorySizeDatePackage
AutoScrollHelper.javaAPI DocAndroid 5.1 API35313Thu Mar 12 22:22:10 GMT 2015com.android.internal.widget

AutoScrollHelper

public abstract class AutoScrollHelper extends Object implements View.OnTouchListener
AutoScrollHelper is a utility class for adding automatic edge-triggered scrolling to Views.

Note: Implementing classes are responsible for overriding the {@link #scrollTargetBy}, {@link #canTargetScrollHorizontally}, and {@link #canTargetScrollVertically} methods. See {@link AbsListViewAutoScroller} for an {@link android.widget.AbsListView} -specific implementation.

Activation

Automatic scrolling starts when the user touches within an activation area. By default, activation areas are defined as the top, left, right, and bottom 20% of the host view's total area. Touching within the top activation area scrolls up, left scrolls to the left, and so on.

As the user touches closer to the extreme edge of the activation area, scrolling accelerates up to a maximum velocity. When using the default edge type, {@link #EDGE_TYPE_INSIDE_EXTEND}, moving outside of the view bounds will scroll at the maximum velocity.

The following activation properties may be configured:

  • Delay after entering activation area before auto-scrolling begins, see {@link #setActivationDelay}. Default value is {@link ViewConfiguration#getTapTimeout()} to avoid conflicting with taps.
  • Location of activation areas, see {@link #setEdgeType}. Default value is {@link #EDGE_TYPE_INSIDE_EXTEND}.
  • Size of activation areas relative to view size, see {@link #setRelativeEdges}. Default value is 20% for both vertical and horizontal edges.
  • Maximum size used to constrain relative size, see {@link #setMaximumEdges}. Default value is {@link #NO_MAX}.

Scrolling

When automatic scrolling is active, the helper will repeatedly call {@link #scrollTargetBy} to apply new scrolling offsets.

The following scrolling properties may be configured:

  • Acceleration ramp-up duration, see {@link #setRampUpDuration}. Default value is 500 milliseconds.
  • Acceleration ramp-down duration, see {@link #setRampDownDuration}. Default value is 500 milliseconds.
  • Target velocity relative to view size, see {@link #setRelativeVelocity}. Default value is 100% per second for both vertical and horizontal.
  • Minimum velocity used to constrain relative velocity, see {@link #setMinimumVelocity}. When set, scrolling will accelerate to the larger of either this value or the relative target value. Default value is approximately 5 centimeters or 315 dips per second.
  • Maximum velocity used to constrain relative velocity, see {@link #setMaximumVelocity}. Default value is approximately 25 centimeters or 1575 dips per second.

Fields Summary
public static final float
RELATIVE_UNSPECIFIED
Constant passed to {@link #setRelativeEdges} or {@link #setRelativeVelocity}. Using this value ensures that the computed relative value is ignored and the absolute maximum value is always used.
public static final float
NO_MAX
Constant passed to {@link #setMaximumEdges}, {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this value ensures that the computed relative value is always used without constraining to a particular minimum or maximum value.
public static final float
NO_MIN
Constant passed to {@link #setMaximumEdges}, or {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this value ensures that the computed relative value is always used without constraining to a particular minimum or maximum value.
public static final int
EDGE_TYPE_INSIDE
Edge type that specifies an activation area starting at the view bounds and extending inward. Moving outside the view bounds will stop scrolling.
public static final int
EDGE_TYPE_INSIDE_EXTEND
Edge type that specifies an activation area starting at the view bounds and extending inward. After activation begins, moving outside the view bounds will continue scrolling.
public static final int
EDGE_TYPE_OUTSIDE
Edge type that specifies an activation area starting at the view bounds and extending outward. Moving inside the view bounds will stop scrolling.
private static final int
HORIZONTAL
private static final int
VERTICAL
private final ClampedScroller
mScroller
Scroller used to control acceleration toward maximum velocity.
private final android.view.animation.Interpolator
mEdgeInterpolator
Interpolator used to scale velocity with touch position.
private final android.view.View
mTarget
The view to auto-scroll. Might not be the source of touch events.
private Runnable
mRunnable
Runnable used to animate scrolling.
private float[]
mRelativeEdges
Edge insets used to activate auto-scrolling.
private float[]
mMaximumEdges
Clamping values for edge insets used to activate auto-scrolling.
private int
mEdgeType
The type of edge being used.
private int
mActivationDelay
Delay after entering an activation edge before auto-scrolling begins.
private float[]
mRelativeVelocity
Relative scrolling velocity at maximum edge distance.
private float[]
mMinimumVelocity
Clamping values used for scrolling velocity.
private float[]
mMaximumVelocity
Clamping values used for scrolling velocity.
private boolean
mAlreadyDelayed
Whether to start activation immediately.
private boolean
mNeedsReset
Whether to reset the scroller start time on the next animation.
private boolean
mNeedsCancel
Whether to send a cancel motion event to the target view.
private boolean
mAnimating
Whether the auto-scroller is actively scrolling.
private boolean
mEnabled
Whether the auto-scroller is enabled.
private boolean
mExclusive
Whether the auto-scroller consumes events when scrolling.
private static final int
DEFAULT_EDGE_TYPE
private static final int
DEFAULT_MINIMUM_VELOCITY_DIPS
private static final int
DEFAULT_MAXIMUM_VELOCITY_DIPS
private static final float
DEFAULT_MAXIMUM_EDGE
private static final float
DEFAULT_RELATIVE_EDGE
private static final float
DEFAULT_RELATIVE_VELOCITY
private static final int
DEFAULT_ACTIVATION_DELAY
private static final int
DEFAULT_RAMP_UP_DURATION
private static final int
DEFAULT_RAMP_DOWN_DURATION
Constructors Summary
public AutoScrollHelper(android.view.View target)
Creates a new helper for scrolling the specified target view.

The resulting helper may be configured by chaining setter calls and should be set as a touch listener on the target view.

By default, the helper is disabled and will not respond to touch events until it is enabled using {@link #setEnabled}.

param
target The view to automatically scroll.


                                                                      
       
        mTarget = target;

        final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        final int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
        final int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
        setMaximumVelocity(maxVelocity, maxVelocity);
        setMinimumVelocity(minVelocity, minVelocity);

        setEdgeType(DEFAULT_EDGE_TYPE);
        setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
        setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
        setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
        setActivationDelay(DEFAULT_ACTIVATION_DELAY);
        setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
        setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
    
Methods Summary
public abstract booleancanTargetScrollHorizontally(int direction)
Override this method to return whether the target view can be scrolled horizontally in a certain direction.

param
direction Negative to check scrolling left, positive to check scrolling right.
return
true if the target view is able to horizontally scroll in the specified direction.

public abstract booleancanTargetScrollVertically(int direction)
Override this method to return whether the target view can be scrolled vertically in a certain direction.

param
direction Negative to check scrolling up, positive to check scrolling down.
return
true if the target view is able to vertically scroll in the specified direction.

private voidcancelTargetTouch()
Sends a {@link MotionEvent#ACTION_CANCEL} event to the target view, canceling any ongoing touch events.

        final long eventTime = SystemClock.uptimeMillis();
        final MotionEvent cancel = MotionEvent.obtain(
                eventTime, eventTime, MotionEvent.ACTION_CANCEL, 0, 0, 0);
        mTarget.onTouchEvent(cancel);
        cancel.recycle();
    
private floatcomputeTargetVelocity(int direction, float coordinate, float srcSize, float dstSize)

        final float relativeEdge = mRelativeEdges[direction];
        final float maximumEdge = mMaximumEdges[direction];
        final float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
        if (value == 0) {
            // The edge in this direction is not activated.
            return 0;
        }

        final float relativeVelocity = mRelativeVelocity[direction];
        final float minimumVelocity = mMinimumVelocity[direction];
        final float maximumVelocity = mMaximumVelocity[direction];
        final float targetVelocity = relativeVelocity * dstSize;

        // Target velocity is adjusted for interpolated edge position, then
        // clamped to the minimum and maximum values. Later, this value will be
        // adjusted for time-based acceleration.
        if (value > 0) {
            return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
        } else {
            return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
        }
    
private static intconstrain(int value, int min, int max)

        if (value > max) {
            return max;
        } else if (value < min) {
            return min;
        } else {
            return value;
        }
    
private static floatconstrain(float value, float min, float max)

        if (value > max) {
            return max;
        } else if (value < min) {
            return min;
        } else {
            return value;
        }
    
private floatconstrainEdgeValue(float current, float leading)

        if (leading == 0) {
            return 0;
        }

        switch (mEdgeType) {
            case EDGE_TYPE_INSIDE:
            case EDGE_TYPE_INSIDE_EXTEND:
                if (current < leading) {
                    if (current >= 0) {
                        // Movement up to the edge is scaled.
                        return 1f - current / leading;
                    } else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
                        // Movement beyond the edge is always maximum.
                        return 1f;
                    }
                }
                break;
            case EDGE_TYPE_OUTSIDE:
                if (current < 0) {
                    // Movement beyond the edge is scaled.
                    return current / -leading;
                }
                break;
        }

        return 0;
    
private floatgetEdgeValue(float relativeValue, float size, float maxValue, float current)
Returns the interpolated position of a touch point relative to an edge defined by its relative inset, its maximum absolute inset, and the edge interpolator.

param
relativeValue The size of the inset relative to the total size.
param
size Total size.
param
maxValue The maximum size of the inset, used to clamp (relative * total).
param
current Touch position within within the total size.
return
Interpolated value of the touch position within the edge.

        // For now, leading and trailing edges are always the same size.
        final float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
        final float valueLeading = constrainEdgeValue(current, edgeSize);
        final float valueTrailing = constrainEdgeValue(size - current, edgeSize);
        final float value = (valueTrailing - valueLeading);
        final float interpolated;
        if (value < 0) {
            interpolated = -mEdgeInterpolator.getInterpolation(-value);
        } else if (value > 0) {
            interpolated = mEdgeInterpolator.getInterpolation(value);
        } else {
            return 0;
        }

        return constrain(interpolated, -1, 1);
    
public booleanisEnabled()

return
True if this helper is enabled and responding to touch events.

        return mEnabled;
    
public booleanisExclusive()
Indicates whether the scroll helper handles touch events exclusively during scrolling.

return
True if exclusive handling of touch events during scrolling is enabled, false otherwise.
see
#setExclusive(boolean)

        return mExclusive;
    
public booleanonTouch(android.view.View v, android.view.MotionEvent event)
Handles touch events by activating automatic scrolling, adjusting scroll velocity, or stopping.

If {@link #isExclusive()} is false, always returns false so that the host view may handle touch events. Otherwise, returns true when automatic scrolling is active and false otherwise.

        if (!mEnabled) {
            return false;
        }

        final int action = event.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mNeedsCancel = true;
                mAlreadyDelayed = false;
                // $FALL-THROUGH$
            case MotionEvent.ACTION_MOVE:
                final float xTargetVelocity = computeTargetVelocity(
                        HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());
                final float yTargetVelocity = computeTargetVelocity(
                        VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());
                mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);

                // If the auto scroller was not previously active, but it should
                // be, then update the state and start animations.
                if (!mAnimating && shouldAnimate()) {
                    startAnimating();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                requestStop();
                break;
        }

        return mExclusive && mAnimating;
    
private voidrequestStop()
Requests that the scroll animation slow to a stop. If there is an activation delay, this may occur between posting the animation and actually running it.

        if (mNeedsReset) {
            // The animation has been posted, but hasn't run yet. Manually
            // stopping animation will prevent it from running.
            mAnimating = false;
        } else {
            mScroller.requestStop();
        }
    
public abstract voidscrollTargetBy(int deltaX, int deltaY)
Override this method to scroll the target view by the specified number of pixels.

param
deltaX The number of pixels to scroll by horizontally.
param
deltaY The number of pixels to scroll by vertically.

public com.android.internal.widget.AutoScrollHelpersetActivationDelay(int delayMillis)
Sets the delay after entering an activation edge before activation of auto-scrolling. By default, the activation delay is set to {@link ViewConfiguration#getTapTimeout()}.

Specifying a delay of zero will start auto-scrolling immediately after the touch position enters an activation edge.

param
delayMillis The activation delay in milliseconds.
return
The scroll helper, which may used to chain setter calls.

        mActivationDelay = delayMillis;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetEdgeType(int type)
Sets the activation edge type, one of:
  • {@link #EDGE_TYPE_INSIDE} for edges that respond to touches inside the bounds of the host view. If touch moves outside the bounds, scrolling will stop.
  • {@link #EDGE_TYPE_INSIDE_EXTEND} for inside edges that continued to scroll when touch moves outside the bounds of the host view.
  • {@link #EDGE_TYPE_OUTSIDE} for edges that only respond to touches that move outside the bounds of the host view.

param
type The type of edge to use.
return
The scroll helper, which may used to chain setter calls.

        mEdgeType = type;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetEnabled(boolean enabled)
Sets whether the scroll helper is enabled and should respond to touch events.

param
enabled Whether the scroll helper is enabled.
return
The scroll helper, which may used to chain setter calls.

        if (mEnabled && !enabled) {
            requestStop();
        }

        mEnabled = enabled;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetExclusive(boolean exclusive)
Enables or disables exclusive handling of touch events during scrolling. By default, exclusive handling is disabled and the target view receives all touch events.

When enabled, {@link #onTouch} will return true if the helper is currently scrolling and false otherwise.

param
exclusive True to exclusively handle touch events during scrolling, false to allow the target view to receive all touch events.
return
The scroll helper, which may used to chain setter calls.

        mExclusive = exclusive;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetMaximumEdges(float horizontalMax, float verticalMax)
Sets the absolute maximum edge size.

If relative edge size is not specified, activation edges will always be the maximum edge size. If both relative and maximum edges are specified, the maximum edge will be used to constrain the calculated relative edge size.

param
horizontalMax The maximum horizontal edge size in pixels, or {@link #NO_MAX} to use the unconstrained calculated relative value.
param
verticalMax The maximum vertical edge size in pixels, or {@link #NO_MAX} to use the unconstrained calculated relative value.
return
The scroll helper, which may used to chain setter calls.

        mMaximumEdges[HORIZONTAL] = horizontalMax;
        mMaximumEdges[VERTICAL] = verticalMax;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetMaximumVelocity(float horizontalMax, float verticalMax)
Sets the absolute maximum scrolling velocity.

If relative velocity is not specified, scrolling will always reach the same maximum velocity. If both relative and maximum velocities are specified, the maximum velocity will be used to clamp the calculated relative velocity.

param
horizontalMax The maximum horizontal scrolling velocity, or {@link #NO_MAX} to leave the relative value unconstrained.
param
verticalMax The maximum vertical scrolling velocity, or {@link #NO_MAX} to leave the relative value unconstrained.
return
The scroll helper, which may used to chain setter calls.

        mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000f;
        mMaximumVelocity[VERTICAL] = verticalMax / 1000f;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetMinimumVelocity(float horizontalMin, float verticalMin)
Sets the absolute minimum scrolling velocity.

If both relative and minimum velocities are specified, the minimum velocity will be used to clamp the calculated relative velocity.

param
horizontalMin The minimum horizontal scrolling velocity, or {@link #NO_MIN} to leave the relative value unconstrained.
param
verticalMin The minimum vertical scrolling velocity, or {@link #NO_MIN} to leave the relative value unconstrained.
return
The scroll helper, which may used to chain setter calls.

        mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000f;
        mMinimumVelocity[VERTICAL] = verticalMin / 1000f;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetRampDownDuration(int durationMillis)
Sets the amount of time after de-activation of auto-scrolling that is takes to slow to a stop.

Specifying a duration greater than zero prevents sudden jumps in velocity.

param
durationMillis The ramp-down duration in milliseconds.
return
The scroll helper, which may used to chain setter calls.

        mScroller.setRampDownDuration(durationMillis);
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetRampUpDuration(int durationMillis)
Sets the amount of time after activation of auto-scrolling that is takes to reach target velocity for the current touch position.

Specifying a duration greater than zero prevents sudden jumps in velocity.

param
durationMillis The ramp-up duration in milliseconds.
return
The scroll helper, which may used to chain setter calls.

        mScroller.setRampUpDuration(durationMillis);
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetRelativeEdges(float horizontal, float vertical)
Sets the activation edge size relative to the host view's dimensions.

If both relative and maximum edges are specified, the maximum edge will be used to constrain the calculated relative edge size.

param
horizontal The horizontal edge size as a fraction of the host view width, or {@link #RELATIVE_UNSPECIFIED} to always use the maximum value.
param
vertical The vertical edge size as a fraction of the host view height, or {@link #RELATIVE_UNSPECIFIED} to always use the maximum value.
return
The scroll helper, which may used to chain setter calls.

        mRelativeEdges[HORIZONTAL] = horizontal;
        mRelativeEdges[VERTICAL] = vertical;
        return this;
    
public com.android.internal.widget.AutoScrollHelpersetRelativeVelocity(float horizontal, float vertical)
Sets the target scrolling velocity relative to the host view's dimensions.

If both relative and maximum velocities are specified, the maximum velocity will be used to clamp the calculated relative velocity.

param
horizontal The target horizontal velocity as a fraction of the host view width per second, or {@link #RELATIVE_UNSPECIFIED} to ignore.
param
vertical The target vertical velocity as a fraction of the host view height per second, or {@link #RELATIVE_UNSPECIFIED} to ignore.
return
The scroll helper, which may used to chain setter calls.

        mRelativeVelocity[HORIZONTAL] = horizontal / 1000f;
        mRelativeVelocity[VERTICAL] = vertical / 1000f;
        return this;
    
private booleanshouldAnimate()

return
whether the target is able to scroll in the requested direction

        final ClampedScroller scroller = mScroller;
        final int verticalDirection = scroller.getVerticalDirection();
        final int horizontalDirection = scroller.getHorizontalDirection();

        return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
                || horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
    
private voidstartAnimating()
Starts the scroll animation.

        if (mRunnable == null) {
            mRunnable = new ScrollAnimationRunnable();
        }

        mAnimating = true;
        mNeedsReset = true;

        if (!mAlreadyDelayed && mActivationDelay > 0) {
            mTarget.postOnAnimationDelayed(mRunnable, mActivationDelay);
        } else {
            mRunnable.run();
        }

        // If we start animating again before the user lifts their finger, we
        // already know it's not a tap and don't need an activation delay.
        mAlreadyDelayed = true;