FileDocCategorySizeDatePackage
SwipeHelper.javaAPI DocAndroid 5.1 API14583Thu Mar 12 22:22:42 GMT 2015com.android.systemui.recents.views

SwipeHelper

public class SwipeHelper extends Object
This class facilitates swipe to dismiss. It defines an interface to be implemented by the by the class hosting the views that need to swiped, and, using this interface, handles touch events and translates / fades / animates the view as it is dismissed.

Fields Summary
static final String
TAG
private static final boolean
SLOW_ANIMATIONS
private static final boolean
CONSTRAIN_SWIPE
private static final boolean
FADE_OUT_DURING_SWIPE
private static final boolean
DISMISS_IF_SWIPED_FAR_ENOUGH
public static final int
X
public static final int
Y
private static android.view.animation.LinearInterpolator
sLinearInterpolator
private float
SWIPE_ESCAPE_VELOCITY
private int
DEFAULT_ESCAPE_ANIMATION_DURATION
private int
MAX_ESCAPE_ANIMATION_DURATION
private static final int
SNAP_ANIM_LEN
public static float
ALPHA_FADE_START
static final float
ALPHA_FADE_END
private float
mMinAlpha
private float
mPagingTouchSlop
Callback
mCallback
private int
mSwipeDirection
private android.view.VelocityTracker
mVelocityTracker
private float
mInitialTouchPos
private boolean
mDragging
private android.view.View
mCurrView
private boolean
mCanCurrViewBeDimissed
private float
mDensityScale
public boolean
mAllowSwipeTowardsStart
public boolean
mAllowSwipeTowardsEnd
private boolean
mRtl
Constructors Summary
public SwipeHelper(int swipeDirection, Callback callback, float densityScale, float pagingTouchSlop)


          
              
        mCallback = callback;
        mSwipeDirection = swipeDirection;
        mVelocityTracker = VelocityTracker.obtain();
        mDensityScale = densityScale;
        mPagingTouchSlop = pagingTouchSlop;
    
Methods Summary
public voidcancelOngoingDrag()

        if (mDragging) {
            if (mCurrView != null) {
                mCallback.onDragCancelled(mCurrView);
                setTranslation(mCurrView, 0);
                mCallback.onSnapBackCompleted(mCurrView);
                mCurrView = null;
            }
            mDragging = false;
        }
    
private android.animation.ObjectAnimatorcreateTranslationAnimation(android.view.View v, float newPos)

        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
                mSwipeDirection == X ? View.TRANSLATION_X : View.TRANSLATION_Y, newPos);
        return anim;
    
private voiddismissChild(android.view.View view, float velocity)

param
view The view to be dismissed
param
velocity The desired pixels/second speed at which the view should move

        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
        float newPos;
        if (velocity < 0
                || (velocity == 0 && getTranslation(view) < 0)
                // if we use the Menu to dismiss an item in landscape, animate up
                || (velocity == 0 && getTranslation(view) == 0 && mSwipeDirection == Y)) {
            newPos = -getSize(view);
        } else {
            newPos = getSize(view);
        }
        int duration = MAX_ESCAPE_ANIMATION_DURATION;
        if (velocity != 0) {
            duration = Math.min(duration,
                                (int) (Math.abs(newPos - getTranslation(view)) *
                                        1000f / Math.abs(velocity)));
        } else {
            duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
        }

        ValueAnimator anim = createTranslationAnimation(view, newPos);
        anim.setInterpolator(sLinearInterpolator);
        anim.setDuration(duration);
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mCallback.onChildDismissed(view);
                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                    view.setAlpha(1.f);
                }
            }
        });
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                    view.setAlpha(getAlphaForOffset(view));
                }
            }
        });
        anim.start();
    
private voidendSwipe(android.view.VelocityTracker velocityTracker)

        velocityTracker.computeCurrentVelocity(1000 /* px/sec */);
        float velocity = getVelocity(velocityTracker);
        float perpendicularVelocity = getPerpendicularVelocity(velocityTracker);
        float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
        float translation = getTranslation(mCurrView);
        // Decide whether to dismiss the current view
        boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
                Math.abs(translation) > 0.6 * getSize(mCurrView);
        boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
                (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
                (velocity > 0) == (translation > 0);

        boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
                && isValidSwipeDirection(translation)
                && (childSwipedFastEnough || childSwipedFarEnough);

        if (dismissChild) {
            // flingadingy
            dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
        } else {
            // snappity
            mCallback.onDragCancelled(mCurrView);
            snapChild(mCurrView, velocity);
        }
    
floatgetAlphaForOffset(android.view.View view)

        float viewSize = getSize(view);
        final float fadeSize = ALPHA_FADE_END * viewSize;
        float result = 1.0f;
        float pos = getTranslation(view);
        if (pos >= viewSize * ALPHA_FADE_START) {
            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
        }
        result = Math.min(result, 1.0f);
        result = Math.max(result, 0f);
        return Math.max(mMinAlpha, result);
    
private floatgetPerpendicularVelocity(android.view.VelocityTracker vt)

        return mSwipeDirection == X ? vt.getYVelocity() :
                vt.getXVelocity();
    
private floatgetPos(android.view.MotionEvent ev)

        return mSwipeDirection == X ? ev.getX() : ev.getY();
    
private floatgetSize(android.view.View v)

        final DisplayMetrics dm = v.getContext().getResources().getDisplayMetrics();
        return mSwipeDirection == X ? dm.widthPixels : dm.heightPixels;
    
private floatgetTranslation(android.view.View v)

        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
    
private floatgetVelocity(android.view.VelocityTracker vt)

        return mSwipeDirection == X ? vt.getXVelocity() :
                vt.getYVelocity();
    
public static booleanisLayoutRtl(android.view.View view)
Determines whether the given view has RTL layout.

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            return View.LAYOUT_DIRECTION_RTL == view.getLayoutDirection();
        } else {
            return false;
        }
    
private booleanisValidSwipeDirection(float amount)

        if (mSwipeDirection == X) {
            if (mRtl) {
                return (amount <= 0) ? mAllowSwipeTowardsEnd : mAllowSwipeTowardsStart;
            } else {
                return (amount <= 0) ? mAllowSwipeTowardsStart : mAllowSwipeTowardsEnd;
            }
        }

        // Vertical swipes are always valid.
        return true;
    
public booleanonInterceptTouchEvent(android.view.MotionEvent ev)

        final int action = ev.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDragging = false;
                mCurrView = mCallback.getChildAtPosition(ev);
                mVelocityTracker.clear();
                if (mCurrView != null) {
                    mRtl = isLayoutRtl(mCurrView);
                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                    mVelocityTracker.addMovement(ev);
                    mInitialTouchPos = getPos(ev);
                } else {
                    mCanCurrViewBeDimissed = false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mCurrView != null) {
                    mVelocityTracker.addMovement(ev);
                    float pos = getPos(ev);
                    float delta = pos - mInitialTouchPos;
                    if (Math.abs(delta) > mPagingTouchSlop) {
                        mCallback.onBeginDrag(mCurrView);
                        mDragging = true;
                        mInitialTouchPos = pos - getTranslation(mCurrView);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mDragging = false;
                mCurrView = null;
                break;
        }
        return mDragging;
    
public booleanonTouchEvent(android.view.MotionEvent ev)

        if (!mDragging) {
            if (!onInterceptTouchEvent(ev)) {
                return mCanCurrViewBeDimissed;
            }
        }

        mVelocityTracker.addMovement(ev);
        final int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_OUTSIDE:
            case MotionEvent.ACTION_MOVE:
                if (mCurrView != null) {
                    float delta = getPos(ev) - mInitialTouchPos;
                    setSwipeAmount(delta);
                    mCallback.onSwipeChanged(mCurrView, delta);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mCurrView != null) {
                    endSwipe(mVelocityTracker);
                }
                break;
        }
        return true;
    
public voidresetTranslation(android.view.View v)

        setTranslation(v, 0);
    
public voidsetDensityScale(float densityScale)

        mDensityScale = densityScale;
    
public voidsetMinAlpha(float minAlpha)

        mMinAlpha = minAlpha;
    
public voidsetPagingTouchSlop(float pagingTouchSlop)

        mPagingTouchSlop = pagingTouchSlop;
    
private voidsetSwipeAmount(float amount)

        // don't let items that can't be dismissed be dragged more than
        // maxScrollDistance
        if (CONSTRAIN_SWIPE
                && (!isValidSwipeDirection(amount) || !mCallback.canChildBeDismissed(mCurrView))) {
            float size = getSize(mCurrView);
            float maxScrollDistance = 0.15f * size;
            if (Math.abs(amount) >= size) {
                amount = amount > 0 ? maxScrollDistance : -maxScrollDistance;
            } else {
                amount = maxScrollDistance * (float) Math.sin((amount/size)*(Math.PI/2));
            }
        }
        setTranslation(mCurrView, amount);
        if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
            float alpha = getAlphaForOffset(mCurrView);
            mCurrView.setAlpha(alpha);
        }
    
private voidsetTranslation(android.view.View v, float translate)

        if (mSwipeDirection == X) {
            v.setTranslationX(translate);
        } else {
            v.setTranslationY(translate);
        }
    
private voidsnapChild(android.view.View view, float velocity)

        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
        ValueAnimator anim = createTranslationAnimation(view, 0);
        int duration = SNAP_ANIM_LEN;
        anim.setDuration(duration);
        anim.setInterpolator(RecentsConfiguration.getInstance().linearOutSlowInInterpolator);
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                    view.setAlpha(getAlphaForOffset(view));
                }
                mCallback.onSwipeChanged(mCurrView, view.getTranslationX());
            }
        });
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
                    view.setAlpha(1.0f);
                }
                mCallback.onSnapBackCompleted(view);
            }
        });
        anim.start();