Android Open Source - appboy-android-sdk Swipe Dismiss Touch Listener






From Project

Back to project page appboy-android-sdk.

License

The source code is released under:

Copyright (c) 2014 Appboy, Inc. All rights reserved. * Use of source code or binaries contained within Appboy's Android SDK is permitted only to enable use of the Appboy platform by customers of Appb...

If you think the Android project appboy-android-sdk listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.appboy.ui.slideups;
//from   w  ww  .  j a v  a 2  s. co  m
/*
 * Copyright 2013 Google Inc.
 *
 * 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.
 */

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;

/**
 * A {@link android.view.View.OnTouchListener} that makes any {@link android.view.View} dismissable when the
 * user swipes (drags her finger) horizontally across the view.
 *
 * <p><em>For {@link android.widget.ListView} list items that don't manage their own touch events
 * (i.e. you're using
 * {@link android.widget.ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)}
 * or an equivalent listener on {@link android.app.ListActivity} or
 * {@link android.app.ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.</em></p>
 *
 * <p>Example usage:</p>
 *
 * <pre>
 * view.setOnTouchListener(new SwipeDismissTouchListener(
 *         view,
 *         null, // Optional token/cookie object
 *         new SwipeDismissTouchListener.OnDismissCallback() {
 *             public void onDismiss(View view, Object token) {
 *                 parent.removeView(view);
 *             }
 *         }));
 * </pre>
 *
 * <p>This class Requires API level 12 or later due to use of {@link
 * android.view.ViewPropertyAnimator}.</p>
 *
 * @see SwipeDismissListViewTouchListener
 */
public class SwipeDismissTouchListener implements View.OnTouchListener {
  // Cached ViewConfiguration and system-wide constant values
  private int mSlop;
  private int mMinFlingVelocity;
  private int mMaxFlingVelocity;
  private long mAnimationTime;

  // Fixed properties
  private View mView;
  private DismissCallbacks mCallbacks;
  private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero

  // Transient properties
  private float mDownX;
  private float mDownY;
  private boolean mSwiping;
  private int mSwipingSlop;
  private Object mToken;
  private VelocityTracker mVelocityTracker;
  private float mTranslationX;

  /**
   * The callback interface used by {@link SwipeDismissTouchListener} to inform its client
   * about a successful dismissal of the view for which it was created.
   */
  public interface DismissCallbacks {
    /**
     * Called to determine whether the view can be dismissed.
     */
    boolean canDismiss(Object token);

    /**
     * Called when the user has indicated they she would like to dismiss the view.
     *
     * @param view  The originating {@link View} to be dismissed.
     * @param token The optional token passed to this object's constructor.
     */
    void onDismiss(View view, Object token);
  }

  /**
   * Constructs a new swipe-to-dismiss touch listener for the given view.
   *
   * @param view     The view to make dismissable.
   * @param token    An optional token/cookie object to be passed through to the callback.
   * @param callbacks The callback to trigger when the user has indicated that she would like to
   *                 dismiss this view.
   */
  public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) {
    ViewConfiguration vc = ViewConfiguration.get(view.getContext());
    mSlop = vc.getScaledTouchSlop();
    mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
    mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
    mAnimationTime = view.getContext().getResources().getInteger(
        android.R.integer.config_shortAnimTime);
    mView = view;
    mToken = token;
    mCallbacks = callbacks;
  }

  @TargetApi(12)
  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {
    // offset because the view is translated during swipe
    motionEvent.offsetLocation(mTranslationX, 0);

    if (mViewWidth < 2) {
      mViewWidth = mView.getWidth();
    }

    switch (motionEvent.getActionMasked()) {
      case MotionEvent.ACTION_DOWN: {
        // TODO: ensure this is a finger, and set a flag
        mDownX = motionEvent.getRawX();
        mDownY = motionEvent.getRawY();
        if (mCallbacks.canDismiss(mToken)) {
          mVelocityTracker = VelocityTracker.obtain();
          mVelocityTracker.addMovement(motionEvent);
        }
        return false;
      }

      case MotionEvent.ACTION_UP: {
        if (mVelocityTracker == null) {
          break;
        }

        float deltaX = motionEvent.getRawX() - mDownX;
        mVelocityTracker.addMovement(motionEvent);
        mVelocityTracker.computeCurrentVelocity(1000);
        float velocityX = mVelocityTracker.getXVelocity();
        float absVelocityX = Math.abs(velocityX);
        float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
        boolean dismiss = false;
        boolean dismissRight = false;
        if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
          dismiss = true;
          dismissRight = deltaX > 0;
        } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
            && absVelocityY < absVelocityX
            && absVelocityY < absVelocityX && mSwiping) {
          // dismiss only if flinging in the same direction as dragging
          dismiss = (velocityX < 0) == (deltaX < 0);
          dismissRight = mVelocityTracker.getXVelocity() > 0;
        }
        if (dismiss) {
          // dismiss
          mView.animate()
              .translationX(dismissRight ? mViewWidth : -mViewWidth)
              .alpha(0)
              .setDuration(mAnimationTime)
              .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                  performDismiss();
                }
              });
        } else if (mSwiping) {
          // cancel
          mView.animate()
              .translationX(0)
              .alpha(1)
              .setDuration(mAnimationTime)
              .setListener(null);
        }
        mVelocityTracker.recycle();
        mVelocityTracker = null;
        mTranslationX = 0;
        mDownX = 0;
        mDownY = 0;
        mSwiping = false;
        break;
      }

      case MotionEvent.ACTION_CANCEL: {
        if (mVelocityTracker == null) {
          break;
        }

        mView.animate()
            .translationX(0)
            .alpha(1)
            .setDuration(mAnimationTime)
            .setListener(null);
        mVelocityTracker.recycle();
        mVelocityTracker = null;
        mTranslationX = 0;
        mDownX = 0;
        mDownY = 0;
        mSwiping = false;
        break;
      }

      case MotionEvent.ACTION_MOVE: {
        if (mVelocityTracker == null) {
          break;
        }

        mVelocityTracker.addMovement(motionEvent);
        float deltaX = motionEvent.getRawX() - mDownX;
        float deltaY = motionEvent.getRawY() - mDownY;
        if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
          mSwiping = true;
          mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
          mView.getParent().requestDisallowInterceptTouchEvent(true);

          // Cancel listview's touch
          MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
          cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
              (motionEvent.getActionIndex() <<
                  MotionEvent.ACTION_POINTER_INDEX_SHIFT));
          mView.onTouchEvent(cancelEvent);
          cancelEvent.recycle();
        }

        if (mSwiping) {
          mTranslationX = deltaX;
          mView.setTranslationX(deltaX - mSwipingSlop);
          // TODO: use an ease-out interpolator or such
//                    mView.setAlpha(Math.max(0f, Math.min(1f,
//                            1f - 2f * Math.abs(deltaX) / mViewWidth)));
          return true;
        }
        break;
      }
    }
    return false;
  }

  @TargetApi(12)
  public void performDismiss() {
    // Animate the dismissed view to zero-height and then fire the dismiss callback.
    // This triggers layout on each animation frame; in the future we may want to do something
    // smarter and more performant.

    final ViewGroup.LayoutParams lp = mView.getLayoutParams();
    final int originalHeight = mView.getHeight();

    ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);

    animator.addListener(new AnimatorListenerAdapter() {
      @Override
      public void onAnimationEnd(Animator animation) {
        mCallbacks.onDismiss(mView, mToken);
        // Reset view presentation
        mView.setAlpha(1f);
        mView.setTranslationX(0);
        lp.height = originalHeight;
        mView.setLayoutParams(lp);
      }
    });

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        lp.height = (Integer) valueAnimator.getAnimatedValue();
        mView.setLayoutParams(lp);
      }
    });

    animator.start();
  }
}




Java Source Code List

com.android.vending.billing.utils.Base64DecoderException.java
com.android.vending.billing.utils.Base64.java
com.android.vending.billing.utils.IabException.java
com.android.vending.billing.utils.IabHelper.java
com.android.vending.billing.utils.IabResult.java
com.android.vending.billing.utils.Inventory.java
com.android.vending.billing.utils.Purchase.java
com.android.vending.billing.utils.Security.java
com.android.vending.billing.utils.SkuDetails.java
com.appboy.AppboyAdmReceiver.java
com.appboy.AppboyGcmReceiver.java
com.appboy.AppboyNotificationUtils.java
com.appboy.helloworld.HelloAppboyActivity.java
com.appboy.sample.AppboyBroadcastReceiver.java
com.appboy.sample.AppboyFragmentActivity.java
com.appboy.sample.CustomAppboyNavigator.java
com.appboy.sample.CustomSlideupManagerListener.java
com.appboy.sample.CustomSlideupViewFactory.java
com.appboy.sample.DecisionFragment.java
com.appboy.sample.DroidBoyActivity.java
com.appboy.sample.DroidGirlActivity.java
com.appboy.sample.DroidboyApplication.java
com.appboy.sample.FeedCategoriesFragment.java
com.appboy.sample.FeedFragmentActivity.java
com.appboy.sample.FeedbackFragmentActivity.java
com.appboy.sample.PreferencesActivity.java
com.appboy.sample.SlideupTesterActivity.java
com.appboy.sample.Test.java
com.appboy.sample.UserProfileDialog.java
com.appboy.sample.util.SharedPrefsUtil.java
com.appboy.ui.AppboyFeedFragment.java
com.appboy.ui.AppboyFeedbackFragment.java
com.appboy.ui.AppboyNavigator.java
com.appboy.ui.AppboyWebViewActivity.java
com.appboy.ui.actions.ActionFactory.java
com.appboy.ui.actions.ActivityAction.java
com.appboy.ui.actions.GooglePlayAppDetailsAction.java
com.appboy.ui.actions.IAction.java
com.appboy.ui.actions.ViewAction.java
com.appboy.ui.actions.WebAction.java
com.appboy.ui.activities.AppboyBaseActivity.java
com.appboy.ui.activities.AppboyBaseFragmentActivity.java
com.appboy.ui.activities.AppboyFeedActivity.java
com.appboy.ui.adapters.AppboyListAdapter.java
com.appboy.ui.configuration.XmlUIConfigurationProvider.java
com.appboy.ui.slideups.AppboySlideupManager.java
com.appboy.ui.slideups.ISlideupManagerListener.java
com.appboy.ui.slideups.ISlideupViewFactory.java
com.appboy.ui.slideups.ISlideupViewLifecycleListener.java
com.appboy.ui.slideups.SlideupCloser.java
com.appboy.ui.slideups.SlideupOperation.java
com.appboy.ui.slideups.SlideupViewWrapper.java
com.appboy.ui.slideups.SwipeDismissTouchListener.java
com.appboy.ui.slideups.TouchAwareSwipeDismissTouchListener.java
com.appboy.ui.support.DrawingUtils.java
com.appboy.ui.support.StringUtils.java
com.appboy.ui.support.UriUtils.java
com.appboy.ui.support.ViewUtils.java
com.appboy.ui.widget.BannerImageCardView.java
com.appboy.ui.widget.BaseCardView.java
com.appboy.ui.widget.CaptionedImageCardView.java
com.appboy.ui.widget.CrossPromotionSmallCardView.java
com.appboy.ui.widget.DefaultCardView.java
com.appboy.ui.widget.ShortNewsCardView.java
com.appboy.ui.widget.StarRatingView.java
com.appboy.ui.widget.TextAnnouncementCardView.java