Merge "Use autoscroller to handle drag scrolling" into jb-ub-gel-agar
This commit is contained in:
committed by
Android (Google) Code Review
commit
70c987a0fe
@@ -102,9 +102,6 @@
|
||||
<!-- When dragging an item, how much bigger (fixed dps) the dragged view
|
||||
should be. If 0, it will not be scaled at all. -->
|
||||
<dimen name="dragViewScale">12dp</dimen>
|
||||
<dimen name="min_scroll_band_size">48dp</dimen>
|
||||
<integer name="scroll_band_factor">20</integer>
|
||||
<integer name="scroll_band_max_factor">30</integer>
|
||||
|
||||
<!-- Padding applied to AppWidget previews -->
|
||||
<dimen name="app_widget_preview_padding_left">16dp</dimen>
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.AbsListView;
|
||||
|
||||
class AutoScroller implements View.OnTouchListener, Runnable {
|
||||
private static final int SCALE_RELATIVE = 0;
|
||||
private static final int SCALE_ABSOLUTE = 1;
|
||||
|
||||
private final View mTarget;
|
||||
private final RampUpScroller mScroller;
|
||||
|
||||
/** Interpolator used to scale velocity with touch position, may be null. */
|
||||
private Interpolator mEdgeInterpolator = new AccelerateInterpolator();
|
||||
|
||||
/**
|
||||
* Type of maximum velocity scaling to use, one of:
|
||||
* <ul>
|
||||
* <li>{@link #SCALE_RELATIVE}
|
||||
* <li>{@link #SCALE_ABSOLUTE}
|
||||
* </ul>
|
||||
*/
|
||||
private int mMaxVelocityScale = SCALE_RELATIVE;
|
||||
|
||||
/**
|
||||
* Type of activation edge scaling to use, one of:
|
||||
* <ul>
|
||||
* <li>{@link #SCALE_RELATIVE}
|
||||
* <li>{@link #SCALE_ABSOLUTE}
|
||||
* </ul>
|
||||
*/
|
||||
private int mActivationEdgeScale = SCALE_RELATIVE;
|
||||
|
||||
/** Edge insets used to activate auto-scrolling. */
|
||||
private RectF mActivationEdges = new RectF(0.2f, 0.2f, 0.2f, 0.2f);
|
||||
|
||||
/** Delay after entering an activation edge before auto-scrolling begins. */
|
||||
private int mActivationDelay;
|
||||
|
||||
/** Maximum horizontal scrolling velocity. */
|
||||
private float mMaxVelocityX = 0.001f;
|
||||
|
||||
/** Maximum vertical scrolling velocity. */
|
||||
private float mMaxVelocityY = 0.001f;
|
||||
|
||||
/**
|
||||
* Whether positive insets should also extend beyond the view bounds when
|
||||
* auto-scrolling is already active. This allows a user to start scrolling
|
||||
* at an inside edge, then move beyond the edge and continue scrolling.
|
||||
*/
|
||||
private boolean mExtendsBeyondEdges = true;
|
||||
|
||||
/** Whether to start activation immediately. */
|
||||
private boolean mSkipDelay;
|
||||
|
||||
/** Whether to reset the scroller start time on the next animation. */
|
||||
private boolean mResetScroller;
|
||||
|
||||
/** Whether the auto-scroller is active. */
|
||||
private boolean mActive;
|
||||
private long[] mScrollStart = new long[2];
|
||||
|
||||
/**
|
||||
* If the event is within this percentage of the edge of the scrolling area,
|
||||
* use accelerated scrolling.
|
||||
*/
|
||||
private float mFastScrollingRange = 0.8f;
|
||||
|
||||
/**
|
||||
* Duration of time spent in accelerated scrolling area before reaching
|
||||
* maximum velocity
|
||||
*/
|
||||
private float mDurationToMax = 2500f;
|
||||
|
||||
private static final int X = 0;
|
||||
private static final int Y = 1;
|
||||
|
||||
public AutoScroller(View target) {
|
||||
mTarget = target;
|
||||
mScroller = new RampUpScroller(250);
|
||||
mActivationDelay = ViewConfiguration.getTapTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum scrolling velocity as a fraction of the host view size
|
||||
* per second. For example, a maximum Y velocity of 1 would scroll one
|
||||
* vertical page per second. By default, both values are 1.
|
||||
*
|
||||
* @param x The maximum X velocity as a fraction of the host view width per
|
||||
* second.
|
||||
* @param y The maximum Y velocity as a fraction of the host view height per
|
||||
* second.
|
||||
*/
|
||||
public void setMaximumVelocityRelative(float x, float y) {
|
||||
mMaxVelocityScale = SCALE_RELATIVE;
|
||||
mMaxVelocityX = x / 1000f;
|
||||
mMaxVelocityY = y / 1000f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum scrolling velocity as an absolute pixel distance per
|
||||
* second. For example, a maximum Y velocity of 100 would scroll one hundred
|
||||
* pixels per second.
|
||||
*
|
||||
* @param x The maximum X velocity as a fraction of the host view width per
|
||||
* second.
|
||||
* @param y The maximum Y velocity as a fraction of the host view height per
|
||||
* second.
|
||||
*/
|
||||
public void setMaximumVelocityAbsolute(float x, float y) {
|
||||
mMaxVelocityScale = SCALE_ABSOLUTE;
|
||||
mMaxVelocityX = x / 1000f;
|
||||
mMaxVelocityY = y / 1000f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the delay after entering an activation edge before activation of
|
||||
* auto-scrolling. By default, the activation delay is set to
|
||||
* {@link ViewConfiguration#getTapTimeout()}.
|
||||
*
|
||||
* @param delayMillis The delay in milliseconds.
|
||||
*/
|
||||
public void setActivationDelay(int delayMillis) {
|
||||
mActivationDelay = delayMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the activation edges in pixels. Edges are treated as insets, so
|
||||
* positive values expand into the view bounds while negative values extend
|
||||
* outside the bounds.
|
||||
*
|
||||
* @param l The left activation edge, in pixels.
|
||||
* @param t The top activation edge, in pixels.
|
||||
* @param r The right activation edge, in pixels.
|
||||
* @param b The bottom activation edge, in pixels.
|
||||
*/
|
||||
public void setEdgesAbsolute(int l, int t, int r, int b) {
|
||||
mActivationEdgeScale = SCALE_ABSOLUTE;
|
||||
mActivationEdges.set(l, t, r, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether positive insets should also extend beyond the view bounds when
|
||||
* auto-scrolling is already active. This allows a user to start scrolling
|
||||
* at an inside edge, then move beyond the edge and continue scrolling.
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
public void setExtendsBeyondEdges(boolean e) {
|
||||
mExtendsBeyondEdges = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the activation edges as fractions of the host view size. Edges are
|
||||
* treated as insets, so positive values expand into the view bounds while
|
||||
* negative values extend outside the bounds. By default, all values are
|
||||
* 0.25.
|
||||
*
|
||||
* @param l The left activation edge, as a fraction of view size.
|
||||
* @param t The top activation edge, as a fraction of view size.
|
||||
* @param r The right activation edge, as a fraction of view size.
|
||||
* @param b The bottom activation edge, as a fraction of view size.
|
||||
*/
|
||||
public void setEdgesRelative(float l, float t, float r, float b) {
|
||||
mActivationEdgeScale = SCALE_RELATIVE;
|
||||
mActivationEdges.set(l, t, r, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Interpolator} used for scaling touches within activation
|
||||
* edges. By default, uses the {@link AccelerateInterpolator} to gradually
|
||||
* speed up scrolling.
|
||||
*
|
||||
* @param edgeInterpolator The interpolator to use for activation edges, or
|
||||
* {@code null} to use a fixed velocity during auto-scrolling.
|
||||
*/
|
||||
public void setEdgeInterpolator(Interpolator edgeInterpolator) {
|
||||
mEdgeInterpolator = edgeInterpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop tracking scrolling.
|
||||
*/
|
||||
public void stop() {
|
||||
stop(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the rectangle defining the drawing region for the object used to
|
||||
* trigger drag scrolling.
|
||||
*
|
||||
* @param v View on which the scrolling regions are defined
|
||||
* @param r Rect defining the drawing bounds of the object being dragged
|
||||
* @return whether the event was handled
|
||||
*/
|
||||
public boolean onTouch(View v, Rect r) {
|
||||
MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, r.left, r.top, 0);
|
||||
return onTouch(v, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
final int action = event.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final int sourceWidth = v.getWidth();
|
||||
final int sourceHeight = v.getHeight();
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
final float l;
|
||||
final float t;
|
||||
final float r;
|
||||
final float b;
|
||||
final RectF activationEdges = mActivationEdges;
|
||||
if (mActivationEdgeScale == SCALE_ABSOLUTE) {
|
||||
l = activationEdges.left;
|
||||
t = activationEdges.top;
|
||||
r = activationEdges.right;
|
||||
b = activationEdges.bottom;
|
||||
} else {
|
||||
l = activationEdges.left * sourceWidth;
|
||||
t = activationEdges.top * sourceHeight;
|
||||
r = activationEdges.right * sourceWidth;
|
||||
b = activationEdges.bottom * sourceHeight;
|
||||
}
|
||||
|
||||
final float maxVelX;
|
||||
final float maxVelY;
|
||||
if (mMaxVelocityScale == SCALE_ABSOLUTE) {
|
||||
maxVelX = mMaxVelocityX;
|
||||
maxVelY = mMaxVelocityY;
|
||||
} else {
|
||||
maxVelX = mMaxVelocityX * mTarget.getWidth();
|
||||
maxVelY = mMaxVelocityY * mTarget.getHeight();
|
||||
}
|
||||
|
||||
final float velocityX = getEdgeVelocity(X, l, r, x, sourceWidth, event);
|
||||
final float velocityY = getEdgeVelocity(Y, t, b, y, sourceHeight, event);
|
||||
mScroller.setTargetVelocity(velocityX * maxVelX, velocityY * maxVelY);
|
||||
|
||||
if ((velocityX != 0 || velocityY != 0) && !mActive) {
|
||||
mActive = true;
|
||||
mResetScroller = true;
|
||||
if (mSkipDelay) {
|
||||
mTarget.postOnAnimation(this);
|
||||
} else {
|
||||
mSkipDelay = true;
|
||||
mTarget.postOnAnimationDelayed(this, mActivationDelay);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
stop(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param leading Size of the leading activation inset.
|
||||
* @param trailing Size of the trailing activation inset.
|
||||
* @param current Position within within the total area.
|
||||
* @param size Size of the total area.
|
||||
* @return The fraction of the activation area.
|
||||
*/
|
||||
private float getEdgeVelocity(int dir, float leading, float trailing,
|
||||
float current, float size, MotionEvent ev) {
|
||||
float valueLeading = 0;
|
||||
if (leading > 0) {
|
||||
if (current < leading) {
|
||||
if (current > 0) {
|
||||
// Movement up to the edge is scaled.
|
||||
valueLeading = 1f - current / leading;
|
||||
} else if (mActive && mExtendsBeyondEdges) {
|
||||
// Movement beyond the edge is always maximum.
|
||||
valueLeading = 1f;
|
||||
}
|
||||
}
|
||||
} else if (leading < 0) {
|
||||
if (current < 0) {
|
||||
// Movement beyond the edge is scaled.
|
||||
valueLeading = current / leading;
|
||||
}
|
||||
}
|
||||
|
||||
float valueTrailing = 0;
|
||||
if (trailing > 0) {
|
||||
if (current > size - trailing) {
|
||||
if (current < size) {
|
||||
// Movement up to the edge is scaled.
|
||||
valueTrailing = 1f - (size - current) / trailing;
|
||||
} else if (mActive && mExtendsBeyondEdges) {
|
||||
// Movement beyond the edge is always maximum.
|
||||
valueTrailing = 1f;
|
||||
}
|
||||
}
|
||||
} else if (trailing < 0) {
|
||||
if (current > size) {
|
||||
// Movement beyond the edge is scaled.
|
||||
valueTrailing = (size - current) / trailing;
|
||||
}
|
||||
}
|
||||
|
||||
float value = (valueTrailing - valueLeading);
|
||||
if ((value > mFastScrollingRange || value < -mFastScrollingRange)
|
||||
&& mScrollStart[dir] == 0) {
|
||||
// within auto scrolling area
|
||||
mScrollStart[dir] = ev.getEventTime();
|
||||
} else {
|
||||
// Outside fast scrolling area; reset duration
|
||||
mScrollStart[dir] = 0;
|
||||
}
|
||||
final float duration = (ev.getEventTime() - mScrollStart[dir])/mDurationToMax;
|
||||
final float interpolated;
|
||||
if (value < 0) {
|
||||
if (value < -mFastScrollingRange) {
|
||||
// Close to top; use duration!
|
||||
value += mEdgeInterpolator.getInterpolation(-duration);
|
||||
}
|
||||
interpolated = mEdgeInterpolator == null ? -1
|
||||
: -mEdgeInterpolator.getInterpolation(-value);
|
||||
} else if (value > 0) {
|
||||
// Close to bottom; use duration
|
||||
if (value > mFastScrollingRange) {
|
||||
// Close to bottom; use duration!
|
||||
value += mEdgeInterpolator.getInterpolation(duration);
|
||||
}
|
||||
interpolated = mEdgeInterpolator == null ? 1
|
||||
: mEdgeInterpolator.getInterpolation(value);
|
||||
} else {
|
||||
mScrollStart[dir] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return constrain(interpolated, -1, 1);
|
||||
}
|
||||
|
||||
private static float constrain(float value, float min, float max) {
|
||||
if (value > max) {
|
||||
return max;
|
||||
} else if (value < min) {
|
||||
return min;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops auto-scrolling immediately, optionally reseting the auto-scrolling
|
||||
* delay.
|
||||
*
|
||||
* @param reset Whether to reset the auto-scrolling delay.
|
||||
*/
|
||||
private void stop(boolean reset) {
|
||||
mActive = false;
|
||||
mSkipDelay = !reset;
|
||||
mTarget.removeCallbacks(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mResetScroller) {
|
||||
mResetScroller = false;
|
||||
mScroller.start();
|
||||
}
|
||||
|
||||
final View target = mTarget;
|
||||
final RampUpScroller scroller = mScroller;
|
||||
final float targetVelocityX = scroller.getTargetVelocityX();
|
||||
final float targetVelocityY = scroller.getTargetVelocityY();
|
||||
if ((targetVelocityY == 0 || !target.canScrollVertically(targetVelocityY > 0 ? 1 : -1)
|
||||
&& (targetVelocityX == 0
|
||||
|| !target.canScrollHorizontally(targetVelocityX > 0 ? 1 : -1)))) {
|
||||
stop(false);
|
||||
return;
|
||||
}
|
||||
|
||||
scroller.computeScrollDelta();
|
||||
|
||||
final int deltaX = scroller.getDeltaX();
|
||||
final int deltaY = scroller.getDeltaY();
|
||||
|
||||
if (target instanceof AbsListView) {
|
||||
final AbsListView list = (AbsListView) target;
|
||||
list.smoothScrollBy(deltaY, 0);
|
||||
} else {
|
||||
target.scrollBy(deltaX, deltaY);
|
||||
}
|
||||
|
||||
target.postOnAnimation(this);
|
||||
}
|
||||
}
|
||||
@@ -123,17 +123,16 @@ public class DragController {
|
||||
* Interface to receive notifications when a drag starts or stops
|
||||
*/
|
||||
interface DragListener {
|
||||
|
||||
/**
|
||||
* A drag has begun
|
||||
*
|
||||
*
|
||||
* @param source An object representing where the drag originated
|
||||
* @param info The data associated with the object that is being dragged
|
||||
* @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
|
||||
* or {@link DragController#DRAG_ACTION_COPY}
|
||||
*/
|
||||
void onDragStart(DragSource source, Object info, int dragAction);
|
||||
|
||||
|
||||
/**
|
||||
* The drag has ended
|
||||
*/
|
||||
@@ -514,7 +513,6 @@ public class DragController {
|
||||
if (delegate != null) {
|
||||
dropTarget = delegate;
|
||||
}
|
||||
|
||||
if (mLastDropTarget != dropTarget) {
|
||||
if (mLastDropTarget != null) {
|
||||
mLastDropTarget.onDragExit(mDragObject);
|
||||
@@ -589,6 +587,7 @@ public class DragController {
|
||||
} else {
|
||||
mScrollState = SCROLL_OUTSIDE_ZONE;
|
||||
}
|
||||
handleMoveEvent(dragLayerX, dragLayerY);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
handleMoveEvent(dragLayerX, dragLayerY);
|
||||
|
||||
@@ -123,7 +123,7 @@ public interface DropTarget {
|
||||
* touch happened
|
||||
* @param dragView The DragView that's being dragged around on screen.
|
||||
* @param dragInfo Data associated with the object being dragged
|
||||
*
|
||||
*
|
||||
*/
|
||||
void onDrop(DragObject dragObject);
|
||||
|
||||
|
||||
@@ -107,22 +107,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
FolderEditText mFolderName;
|
||||
private float mFolderIconPivotX;
|
||||
private float mFolderIconPivotY;
|
||||
private long mDragScrollStart;
|
||||
|
||||
/** Interpolator used to scale velocity with touch position, may be null. */
|
||||
private Interpolator mEdgeInterpolator = new AccelerateInterpolator();
|
||||
|
||||
private static final int SCROLL_CUT_OFF_AMOUNT = 60;
|
||||
|
||||
private final float mDurationToMax = 300f;
|
||||
private final float mMaxVelocity = 100000f/1000f;
|
||||
|
||||
// Aim for this amount of target area to trigger scrolling
|
||||
private static float sScrollBandFactor = -1f;
|
||||
private static float sScrollBandMaxFactor = -1f;
|
||||
|
||||
// Min size for the target area to trigger scrolling
|
||||
private static int sMinScrollBandHeight = -1;
|
||||
private static final float MAX_SCROLL_VELOCITY = 1500f;
|
||||
|
||||
private boolean mIsEditingName = false;
|
||||
private InputMethodManager mInputMethodManager;
|
||||
@@ -132,12 +120,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
|
||||
private int DRAG_MODE_NONE = 0;
|
||||
private int DRAG_MODE_REORDER = 1;
|
||||
private int DRAG_MODE_SCROLL_UP = 2;
|
||||
private int DRAG_MODE_SCROLL_DOWN = 3;
|
||||
private int mDragMode = DRAG_MODE_NONE;
|
||||
|
||||
private boolean mDestroyed;
|
||||
|
||||
private AutoScroller mAutoScroller;
|
||||
|
||||
/**
|
||||
* Used to inflate the Workspace from XML.
|
||||
*
|
||||
@@ -191,15 +179,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
if (sHintText == null) {
|
||||
sHintText = res.getString(R.string.folder_hint_text);
|
||||
}
|
||||
if (sMinScrollBandHeight == -1) {
|
||||
sMinScrollBandHeight = res.getDimensionPixelSize(R.dimen.min_scroll_band_size);
|
||||
}
|
||||
if (sScrollBandFactor == -1f) {
|
||||
sScrollBandFactor = res.getInteger(R.integer.scroll_band_factor)/100f;
|
||||
}
|
||||
if (sScrollBandMaxFactor == -1f) {
|
||||
sScrollBandMaxFactor = res.getInteger(R.integer.scroll_band_max_factor)/100f;
|
||||
}
|
||||
mLauncher = (Launcher) context;
|
||||
// We need this view to be focusable in touch mode so that when text editing of the folder
|
||||
// name is complete, we have something to focus on, thus hiding the cursor and giving
|
||||
@@ -236,6 +215,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
mFolderName.setSelectAllOnFocus(true);
|
||||
mFolderName.setInputType(mFolderName.getInputType() |
|
||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
|
||||
mAutoScroller = new AutoScroller(mScrollView);
|
||||
mAutoScroller.setMaximumVelocityAbsolute(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY);
|
||||
mAutoScroller.setExtendsBeyondEdges(false);
|
||||
}
|
||||
|
||||
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
|
||||
@@ -673,83 +655,38 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
|
||||
}
|
||||
|
||||
private Rect getDragObjectDrawingRect(View dragView, float[] r) {
|
||||
final Rect drawingRect = mTempRect;
|
||||
drawingRect.left = (int) r[0];
|
||||
drawingRect.top = (int) r[1];
|
||||
drawingRect.right = drawingRect.left + dragView.getMeasuredWidth();
|
||||
drawingRect.bottom = drawingRect.top + dragView.getMeasuredHeight();
|
||||
return drawingRect;
|
||||
}
|
||||
|
||||
public void onDragOver(DragObject d) {
|
||||
int scrollOffset = mScrollView.getScrollY();
|
||||
int height = getMeasuredHeight();
|
||||
int scrollBandHeight = Math.min((int) (height * sScrollBandMaxFactor),
|
||||
(Math.max(sMinScrollBandHeight, (int) (height * sScrollBandFactor))));
|
||||
float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, null);
|
||||
r[0] -= getPaddingLeft();
|
||||
r[1] -= getPaddingTop();
|
||||
if (!mAutoScroller.onTouch(this, getDragObjectDrawingRect(d.dragView, r))) {
|
||||
mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1] + scrollOffset, 1, 1,
|
||||
mTargetCell);
|
||||
|
||||
mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1] + scrollOffset,
|
||||
1, 1, mTargetCell);
|
||||
|
||||
if (isLayoutRtl()) {
|
||||
mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1;
|
||||
}
|
||||
|
||||
if (r[1] < scrollBandHeight && mScrollView.getScrollY() > 0) {
|
||||
// Scroll up
|
||||
if (mDragMode != DRAG_MODE_SCROLL_UP) {
|
||||
mDragMode = DRAG_MODE_SCROLL_UP;
|
||||
mDragScrollStart = System.currentTimeMillis();
|
||||
scrollUp();
|
||||
if (isLayoutRtl()) {
|
||||
mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1;
|
||||
}
|
||||
mReorderAlarm.cancelAlarm();
|
||||
} else if (r[1] > (getFolderHeight() - scrollBandHeight) && mScrollView.getScrollY() <
|
||||
(mContent.getMeasuredHeight() - mScrollView.getMeasuredHeight())) {
|
||||
if (mDragMode != DRAG_MODE_SCROLL_DOWN) {
|
||||
mDragMode = DRAG_MODE_SCROLL_DOWN;
|
||||
mDragScrollStart = System.currentTimeMillis();
|
||||
scrollDown();
|
||||
if (mTargetCell[0] != mPreviousTargetCell[0]
|
||||
|| mTargetCell[1] != mPreviousTargetCell[1]) {
|
||||
mReorderAlarm.cancelAlarm();
|
||||
mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);
|
||||
mReorderAlarm.setAlarm(REORDER_DELAY);
|
||||
mPreviousTargetCell[0] = mTargetCell[0];
|
||||
mPreviousTargetCell[1] = mTargetCell[1];
|
||||
mDragMode = DRAG_MODE_REORDER;
|
||||
} else {
|
||||
mDragMode = DRAG_MODE_NONE;
|
||||
}
|
||||
mReorderAlarm.cancelAlarm();
|
||||
} else if (mTargetCell[0] != mPreviousTargetCell[0] || mTargetCell[1] != mPreviousTargetCell[1]) {
|
||||
mReorderAlarm.cancelAlarm();
|
||||
mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);
|
||||
mReorderAlarm.setAlarm(REORDER_DELAY);
|
||||
mPreviousTargetCell[0] = mTargetCell[0];
|
||||
mPreviousTargetCell[1] = mTargetCell[1];
|
||||
mDragMode = DRAG_MODE_REORDER;
|
||||
} else {
|
||||
mDragMode = DRAG_MODE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
Runnable mScrollUpRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrollUp();
|
||||
}
|
||||
};
|
||||
|
||||
Runnable mScrollDownRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrollDown();
|
||||
}
|
||||
};
|
||||
|
||||
private int getVelocity() {
|
||||
float duration = (System.currentTimeMillis() - mDragScrollStart)/mDurationToMax;
|
||||
return (int) Math.min(mMaxVelocity,
|
||||
(int) (mEdgeInterpolator.getInterpolation(duration) * mMaxVelocity));
|
||||
}
|
||||
|
||||
private void scrollUp() {
|
||||
if (mDragMode == DRAG_MODE_SCROLL_UP) {
|
||||
mScrollView.setScrollY(mScrollView.getScrollY() - getVelocity());
|
||||
invalidate();
|
||||
post(mScrollUpRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void scrollDown() {
|
||||
if (mDragMode == DRAG_MODE_SCROLL_DOWN) {
|
||||
mScrollView.setScrollY(mScrollView.getScrollY() + getVelocity());
|
||||
invalidate();
|
||||
post(mScrollDownRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,8 +731,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
|
||||
}
|
||||
|
||||
public void onDragExit(DragObject d) {
|
||||
// We only close the folder if this is a true drag exit, ie. not because a drop
|
||||
// has occurred above the folder.
|
||||
// Exiting folder; stop the auto scroller.
|
||||
mAutoScroller.stop();
|
||||
// We only close the folder if this is a true drag exit, ie. not because
|
||||
// a drop has occurred above the folder.
|
||||
if (!d.dragComplete) {
|
||||
mOnExitAlarm.setOnAlarmListener(mOnExitAlarmListener);
|
||||
mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY);
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2013 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.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
/**
|
||||
* Scroller that gradually reaches a target velocity.
|
||||
*/
|
||||
class RampUpScroller {
|
||||
private final Interpolator mInterpolator;
|
||||
private final long mRampUpTime;
|
||||
|
||||
private long mStartTime;
|
||||
private long mDeltaTime;
|
||||
private float mTargetVelocityX;
|
||||
private float mTargetVelocityY;
|
||||
private int mDeltaX;
|
||||
private int mDeltaY;
|
||||
|
||||
/**
|
||||
* Creates a new ramp-up scroller that reaches full velocity after a
|
||||
* specified duration.
|
||||
*
|
||||
* @param rampUpTime Duration before the scroller reaches target velocity.
|
||||
*/
|
||||
public RampUpScroller(long rampUpTime) {
|
||||
mInterpolator = new AccelerateInterpolator();
|
||||
mRampUpTime = rampUpTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the scroller at the current animation time.
|
||||
*/
|
||||
public void start() {
|
||||
mStartTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
mDeltaTime = mStartTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the current scroll deltas. This usually only be called after
|
||||
* starting the scroller with {@link #start()}.
|
||||
*
|
||||
* @see #getDeltaX()
|
||||
* @see #getDeltaY()
|
||||
*/
|
||||
public void computeScrollDelta() {
|
||||
final long currentTime = AnimationUtils.currentAnimationTimeMillis();
|
||||
final long elapsedSinceStart = currentTime - mStartTime;
|
||||
final float scale;
|
||||
if (elapsedSinceStart < mRampUpTime) {
|
||||
scale = mInterpolator.getInterpolation((float) elapsedSinceStart / mRampUpTime);
|
||||
} else {
|
||||
scale = 1f;
|
||||
}
|
||||
|
||||
final long elapsedSinceDelta = currentTime - mDeltaTime;
|
||||
mDeltaTime = currentTime;
|
||||
|
||||
mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
|
||||
mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target velocity for this scroller.
|
||||
*
|
||||
* @param x The target X velocity in pixels per millisecond.
|
||||
* @param y The target Y velocity in pixels per millisecond.
|
||||
*/
|
||||
public void setTargetVelocity(float x, float y) {
|
||||
mTargetVelocityX = x;
|
||||
mTargetVelocityY = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The target X velocity for this scroller.
|
||||
*/
|
||||
public float getTargetVelocityX() {
|
||||
return mTargetVelocityX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The target Y velocity for this scroller.
|
||||
*/
|
||||
public float getTargetVelocityY() {
|
||||
return mTargetVelocityY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance traveled in the X-coordinate computed by the last call to
|
||||
* {@link #computeScrollDelta()}.
|
||||
*/
|
||||
public int getDeltaX() {
|
||||
return mDeltaX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance traveled in the Y-coordinate computed by the last call to
|
||||
* {@link #computeScrollDelta()}.
|
||||
*/
|
||||
public int getDeltaY() {
|
||||
return mDeltaY;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user