Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android側滑效果簡單實現代碼

Android側滑效果簡單實現代碼

編輯:關於Android編程

先看看效果:

首先,導入包:compile files('libs/nineoldandroids-2.4.0.jar')

r然後在main中創建一個widget包。
c創建ViewDragHelper類

public class ViewDragHelper {
 private static final String TAG = "ViewDragHelper";
 public static final int INVALID_POINTER = -1
 public static final int STATE_IDLE = 0;
 public static final int STATE_DRAGGING = 1;
 public static final int STATE_SETTLING = 2;
 public static final int EDGE_LEFT = 1 << 0;
 public static final int EDGE_RIGHT = 1 << 1
 public static final int EDGE_TOP = 1 << 2;
 public static final int EDGE_BOTTOM = 1 << 3
 public static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT | EDGE_BOTTOM;
 public static final int DIRECTION_HORIZONTAL = 1 << 0;
 public static final int DIRECTION_VERTICAL = 1 << 1;
 public static final int DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL;
 private static final int EDGE_SIZE = 20; // dp
 private static final int BASE_SETTLE_DURATION = 256; // ms
 private static final int MAX_SETTLE_DURATION = 600; // ms
 // Current drag state; idle, dragging or settling
 private int mDragState;

 // Distance to travel before a drag may begin
 private int mTouchSlop;

 // Last known position/pointer tracking
 private int mActivePointerId = INVALID_POINTER;
 private float[] mInitialMotionX;
 private float[] mInitialMotionY;
 private float[] mLastMotionX;
 private float[] mLastMotionY;
 private int[] mInitialEdgesTouched;
 private int[] mEdgeDragsInProgress;
 private int[] mEdgeDragsLocked;
 private int mPointersDown;

 private VelocityTracker mVelocityTracker;
 private final float mMaxVelocity;
 private float mMinVelocity;

 private final int mEdgeSize;
 private int mTrackingEdges;

 private final ScrollerCompat mScroller;

 private final Callback mCallback;

 private View mCapturedView;
 private boolean mReleaseInProgress;

 private final ViewGroup mParentView;


 private static final Interpolator sInterpolator = new Interpolator() {
  public float getInterpolation(float t) {
   t -= 1.0f;
   return t * t * t * t * t + 1.0f;
  }
 };

 private final Runnable mSetIdleRunnable = new Runnable() {
  public void run() {
   setDragState(STATE_IDLE);
  }
 };

 private ViewDragHelper(Context context, ViewGroup forParent, Callback cb) {
  if (forParent == null) {
   throw new IllegalArgumentException("Parent view may not be null");
  }
  if (cb == null) {
   throw new IllegalArgumentException("Callback may not be null");
  }

  mParentView = forParent;
  mCallback = cb;

  final ViewConfiguration vc = ViewConfiguration.get(context);
  final float density = context.getResources().getDisplayMetrics().density;
  mEdgeSize = (int) (EDGE_SIZE * density + 0.5f);

  mTouchSlop = vc.getScaledTouchSlop();
  mMaxVelocity = vc.getScaledMaximumFlingVelocity();
  mMinVelocity = vc.getScaledMinimumFlingVelocity();
  mScroller = ScrollerCompat.create(context, sInterpolator);
 }

 public static abstract class Callback {

  public void onViewDragStateChanged(int state) {}

  public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}


  public void onViewCaptured(View capturedChild, int activePointerId) {}


  public void onViewReleased(View releasedChild, float xvel, float yvel) {}

  public void onEdgeTouched(int edgeFlags, int pointerId) {}

  public boolean onEdgeLock(int edgeFlags) {
   return false;
  }

  public void onEdgeDragStarted(int edgeFlags, int pointerId) {}


  public int getOrderedChildIndex(int index) {
   return index;
  }

  public int getViewHorizontalDragRange(View child) {
   return 0;
  }


  public int getViewVerticalDragRange(View child) {
   return 0;
  }

  public abstract boolean tryCaptureView(View child, int pointerId);


  public int clampViewPositionHorizontal(View child, int left, int dx) {
   return 0;
  }

  public int clampViewPositionVertical(View child, int top, int dy) {
   return 0;
  }
 }


 public static ViewDragHelper create(ViewGroup forParent, Callback cb) {
  return new ViewDragHelper(forParent.getContext(), forParent, cb);
 }

 public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
  final ViewDragHelper helper = create(forParent, cb);
  helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
  return helper;
 }

 public void setMinVelocity(float minVel) {
  mMinVelocity = minVel;
 }


 public float getMinVelocity() {
  return mMinVelocity;
 }


 public int getViewDragState() {
  return mDragState;
 }

 public void setEdgeTrackingEnabled(int edgeFlags) {
  mTrackingEdges = edgeFlags;
 }


 public int getEdgeSize() {
  return mEdgeSize;
 }

 public void captureChildView(View childView, int activePointerId) {
  if (childView.getParent() != mParentView) {
   throw new IllegalArgumentException("captureChildView: parameter must be a descendant " +
     "of the ViewDragHelper's tracked parent view (" + mParentView + ")");
  }

  mCapturedView = childView;
  mActivePointerId = activePointerId;
  mCallback.onViewCaptured(childView, activePointerId);
  setDragState(STATE_DRAGGING);
 }

 public View getCapturedView() {
  return mCapturedView;
 }

 public int getActivePointerId() {
  return mActivePointerId;
 }

 public int getTouchSlop() {
  return mTouchSlop;
 }

 public void cancel() {
  mActivePointerId = INVALID_POINTER;
  clearMotionHistory();

  if (mVelocityTracker != null) {
   mVelocityTracker.recycle();
   mVelocityTracker = null;
  }
 }

 public void abort() {
  cancel();
  if (mDragState == STATE_SETTLING) {
   final int oldX = mScroller.getCurrX();
   final int oldY = mScroller.getCurrY();
   mScroller.abortAnimation();
   final int newX = mScroller.getCurrX();
   final int newY = mScroller.getCurrY();
   mCallback.onViewPositionChanged(mCapturedView, newX, newY, newX - oldX, newY - oldY);
  }
  setDragState(STATE_IDLE);
 }


 public boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {
  mCapturedView = child;
  mActivePointerId = INVALID_POINTER;

  boolean continueSliding = forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0);
  if (!continueSliding && mDragState == STATE_IDLE && mCapturedView != null) {
   // If we're in an IDLE state to begin with and aren't moving anywhere, we
   // end up having a non-null capturedView with an IDLE dragState
   mCapturedView = null;
  }

  return continueSliding;
 }


 public boolean settleCapturedViewAt(int finalLeft, int finalTop) {
  if (!mReleaseInProgress) {
   throw new IllegalStateException("Cannot settleCapturedViewAt outside of a call to " +
     "Callback#onViewReleased");
  }

  return forceSettleCapturedViewAt(finalLeft, finalTop,
    (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
    (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId));
 }

 private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {
  final int startLeft = mCapturedView.getLeft();
  final int startTop = mCapturedView.getTop();
  final int dx = finalLeft - startLeft;
  final int dy = finalTop - startTop;

  if (dx == 0 && dy == 0) {
   // Nothing to do. Send callbacks, be done.
   mScroller.abortAnimation();
   setDragState(STATE_IDLE);
   return false;
  }

  final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);
  mScroller.startScroll(startLeft, startTop, dx, dy, duration);

  setDragState(STATE_SETTLING);
  return true;
 }

 private int computeSettleDuration(View child, int dx, int dy, int xvel, int yvel) {
  xvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity);
  yvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity);
  final int absDx = Math.abs(dx);
  final int absDy = Math.abs(dy);
  final int absXVel = Math.abs(xvel);
  final int absYVel = Math.abs(yvel);
  final int addedVel = absXVel + absYVel;
  final int addedDistance = absDx + absDy;

  final float xweight = xvel != 0 ? (float) absXVel / addedVel :
    (float) absDx / addedDistance;
  final float yweight = yvel != 0 ? (float) absYVel / addedVel :
    (float) absDy / addedDistance;

  int xduration = computeAxisDuration(dx, xvel, mCallback.getViewHorizontalDragRange(child));
  int yduration = computeAxisDuration(dy, yvel, mCallback.getViewVerticalDragRange(child));

  return (int) (xduration * xweight + yduration * yweight);
 }

 private int computeAxisDuration(int delta, int velocity, int motionRange) {
  if (delta == 0) {
   return 0;
  }

  final int width = mParentView.getWidth();
  final int halfWidth = width / 2;
  final float distanceRatio = Math.min(1f, (float) Math.abs(delta) / width);
  final float distance = halfWidth + halfWidth *
    distanceInfluenceForSnapDuration(distanceRatio);

  int duration;
  velocity = Math.abs(velocity);
  if (velocity > 0) {
   duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
  } else {
   final float range = (float) Math.abs(delta) / motionRange;
   duration = (int) ((range + 1) * BASE_SETTLE_DURATION);
  }
  return Math.min(duration, MAX_SETTLE_DURATION);
 }


 private static int clampMag(int value, int absMin, int absMax) {
  final int absValue = Math.abs(value);
  if (absValue < absMin) return 0;
  if (absValue > absMax) return value > 0 ? absMax : -absMax;
  return value;
 }


 private static float clampMag(float value, float absMin, float absMax) {
  final float absValue = Math.abs(value);
  if (absValue < absMin) return 0;
  if (absValue > absMax) return value > 0 ? absMax : -absMax;
  return value;
 }

 private static float distanceInfluenceForSnapDuration(float f) {
  f -= 0.5f; // center the values about 0.
  f *= 0.3f * Math.PI / 2.0f;
  return (float) Math.sin(f);
 }


 public void flingCapturedView(int minLeft, int minTop, int maxLeft, int maxTop) {
  if (!mReleaseInProgress) {
   throw new IllegalStateException("Cannot flingCapturedView outside of a call to " +
     "Callback#onViewReleased");
  }

  mScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),
    (int) VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
    (int) VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
    minLeft, maxLeft, minTop, maxTop);

  setDragState(STATE_SETTLING);
 }


 public boolean continueSettling(boolean deferCallbacks) {
  if (mDragState == STATE_SETTLING) {
   boolean keepGoing = mScroller.computeScrollOffset();
   final int x = mScroller.getCurrX();
   final int y = mScroller.getCurrY();
   final int dx = x - mCapturedView.getLeft();
   final int dy = y - mCapturedView.getTop();

   if (dx != 0) {
    mCapturedView.offsetLeftAndRight(dx);
   }
   if (dy != 0) {
    mCapturedView.offsetTopAndBottom(dy);
   }

   if (dx != 0 || dy != 0) {
    mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);
   }

   if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {
    // Close enough. The interpolator/scroller might think we're still moving
    // but the user sure doesn't.
    mScroller.abortAnimation();
    keepGoing = false;
   }

   if (!keepGoing) {
    if (deferCallbacks) {
     mParentView.post(mSetIdleRunnable);
    } else {
     setDragState(STATE_IDLE);
    }
   }
  }

  return mDragState == STATE_SETTLING;
 }


 private void dispatchViewReleased(float xvel, float yvel) {
  mReleaseInProgress = true;
  mCallback.onViewReleased(mCapturedView, xvel, yvel);
  mReleaseInProgress = false;

  if (mDragState == STATE_DRAGGING) {
   // onViewReleased didn't call a method that would have changed this. Go idle.
   setDragState(STATE_IDLE);
  }
 }

 private void clearMotionHistory() {
  if (mInitialMotionX == null) {
   return;
  }
  Arrays.fill(mInitialMotionX, 0);
  Arrays.fill(mInitialMotionY, 0);
  Arrays.fill(mLastMotionX, 0);
  Arrays.fill(mLastMotionY, 0);
  Arrays.fill(mInitialEdgesTouched, 0);
  Arrays.fill(mEdgeDragsInProgress, 0);
  Arrays.fill(mEdgeDragsLocked, 0);
  mPointersDown = 0;
 }

 private void clearMotionHistory(int pointerId) {
  if (mInitialMotionX == null) {
   return;
  }
  mInitialMotionX[pointerId] = 0;
  mInitialMotionY[pointerId] = 0;
  mLastMotionX[pointerId] = 0;
  mLastMotionY[pointerId] = 0;
  mInitialEdgesTouched[pointerId] = 0;
  mEdgeDragsInProgress[pointerId] = 0;
  mEdgeDragsLocked[pointerId] = 0;
  mPointersDown &= ~(1 << pointerId);
 }

 private void ensureMotionHistorySizeForId(int pointerId) {
  if (mInitialMotionX == null || mInitialMotionX.length <= pointerId) {
   float[] imx = new float[pointerId + 1];
   float[] imy = new float[pointerId + 1];
   float[] lmx = new float[pointerId + 1];
   float[] lmy = new float[pointerId + 1];
   int[] iit = new int[pointerId + 1];
   int[] edip = new int[pointerId + 1];
   int[] edl = new int[pointerId + 1];

   if (mInitialMotionX != null) {
    System.arraycopy(mInitialMotionX, 0, imx, 0, mInitialMotionX.length);
    System.arraycopy(mInitialMotionY, 0, imy, 0, mInitialMotionY.length);
    System.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length);
    System.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length);
    System.arraycopy(mInitialEdgesTouched, 0, iit, 0, mInitialEdgesTouched.length);
    System.arraycopy(mEdgeDragsInProgress, 0, edip, 0, mEdgeDragsInProgress.length);
    System.arraycopy(mEdgeDragsLocked, 0, edl, 0, mEdgeDragsLocked.length);
   }

   mInitialMotionX = imx;
   mInitialMotionY = imy;
   mLastMotionX = lmx;
   mLastMotionY = lmy;
   mInitialEdgesTouched = iit;
   mEdgeDragsInProgress = edip;
   mEdgeDragsLocked = edl;
  }
 }

 private void saveInitialMotion(float x, float y, int pointerId) {
  ensureMotionHistorySizeForId(pointerId);
  mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;
  mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;
  mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);
  mPointersDown |= 1 << pointerId;
 }

 private void saveLastMotion(MotionEvent ev) {
  final int pointerCount = MotionEventCompat.getPointerCount(ev);
  for (int i = 0; i < pointerCount; i++) {
   final int pointerId = MotionEventCompat.getPointerId(ev, i);
   final float x = MotionEventCompat.getX(ev, i);
   final float y = MotionEventCompat.getY(ev, i);
   mLastMotionX[pointerId] = x;
   mLastMotionY[pointerId] = y;
  }
 }

 public boolean isPointerDown(int pointerId) {
  return (mPointersDown & 1 << pointerId) != 0;
 }

 void setDragState(int state) {
  mParentView.removeCallbacks(mSetIdleRunnable);
  if (mDragState != state) {
   mDragState = state;
   mCallback.onViewDragStateChanged(state);
   if (mDragState == STATE_IDLE) {
    mCapturedView = null;
   }
  }
 }

 boolean tryCaptureViewForDrag(View toCapture, int pointerId) {
  if (toCapture == mCapturedView && mActivePointerId == pointerId) {
   // Already done!
   return true;
  }
  if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {
   mActivePointerId = pointerId;
   captureChildView(toCapture, pointerId);
   return true;
  }
  return false;
 }


 protected boolean canScroll(View v, boolean checkV, int dx, int dy, int x, int y) {
  if (v instanceof ViewGroup) {
   final ViewGroup group = (ViewGroup) v;
   final int scrollX = v.getScrollX();
   final int scrollY = v.getScrollY();
   final int count = group.getChildCount();
   // Count backwards - let topmost views consume scroll distance first.
   for (int i = count - 1; i >= 0; i--) {
    // TODO: Add versioned support here for transformed views.
    // This will not work for transformed views in Honeycomb+
    final View child = group.getChildAt(i);
    if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
      y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
      canScroll(child, true, dx, dy, x + scrollX - child.getLeft(),
        y + scrollY - child.getTop())) {
     return true;
    }
   }
  }

  return checkV && (ViewCompat.canScrollHorizontally(v, -dx) ||
    ViewCompat.canScrollVertically(v, -dy));
 }

 public boolean shouldInterceptTouchEvent(MotionEvent ev) {
  final int action = MotionEventCompat.getActionMasked(ev);
  final int actionIndex = MotionEventCompat.getActionIndex(ev);

  if (action == MotionEvent.ACTION_DOWN) {
   // Reset things for a new event stream, just in case we didn't get
   // the whole previous stream.
   cancel();
  }

  if (mVelocityTracker == null) {
   mVelocityTracker = VelocityTracker.obtain();
  }
  mVelocityTracker.addMovement(ev);

  switch (action) {
   case MotionEvent.ACTION_DOWN: {
    final float x = ev.getX();
    final float y = ev.getY();
    final int pointerId = MotionEventCompat.getPointerId(ev, 0);
    saveInitialMotion(x, y, pointerId);

    final View toCapture = findTopChildUnder((int) x, (int) y);

    // Catch a settling view if possible.
    if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
     tryCaptureViewForDrag(toCapture, pointerId);
    }

    final int edgesTouched = mInitialEdgesTouched[pointerId];
    if ((edgesTouched & mTrackingEdges) != 0) {
     mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
    }
    break;
   }

   case MotionEventCompat.ACTION_POINTER_DOWN: {
    final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
    final float x = MotionEventCompat.getX(ev, actionIndex);
    final float y = MotionEventCompat.getY(ev, actionIndex);

    saveInitialMotion(x, y, pointerId);

    // A ViewDragHelper can only manipulate one view at a time.
    if (mDragState == STATE_IDLE) {
     final int edgesTouched = mInitialEdgesTouched[pointerId];
     if ((edgesTouched & mTrackingEdges) != 0) {
      mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
     }
    } else if (mDragState == STATE_SETTLING) {
     // Catch a settling view if possible.
     final View toCapture = findTopChildUnder((int) x, (int) y);
     if (toCapture == mCapturedView) {
      tryCaptureViewForDrag(toCapture, pointerId);
     }
    }
    break;
   }

   case MotionEvent.ACTION_MOVE: {
    if (mInitialMotionX == null || mInitialMotionY == null) break;

    // First to cross a touch slop over a draggable view wins. Also report edge drags.
    final int pointerCount = MotionEventCompat.getPointerCount(ev);
    for (int i = 0; i < pointerCount; i++) {
     final int pointerId = MotionEventCompat.getPointerId(ev, i);
     final float x = MotionEventCompat.getX(ev, i);
     final float y = MotionEventCompat.getY(ev, i);
     final float dx = x - mInitialMotionX[pointerId];
     final float dy = y - mInitialMotionY[pointerId];

     final View toCapture = findTopChildUnder((int) x, (int) y);
     final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);
     if (pastSlop) {
      // check the callback's
      // getView[Horizontal|Vertical]DragRange methods to know
      // if you can move at all along an axis, then see if it
      // would clamp to the same value. If you can't move at
      // all in every dimension with a nonzero range, bail.
      final int oldLeft = toCapture.getLeft();
      final int targetLeft = oldLeft + (int) dx;
      final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,
        targetLeft, (int) dx);
      final int oldTop = toCapture.getTop();
      final int targetTop = oldTop + (int) dy;
      final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
        (int) dy);
      final int horizontalDragRange = mCallback.getViewHorizontalDragRange(
        toCapture);
      final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture);
      if ((horizontalDragRange == 0 || horizontalDragRange > 0
        && newLeft == oldLeft) && (verticalDragRange == 0
        || verticalDragRange > 0 && newTop == oldTop)) {
       break;
      }
     }
     reportNewEdgeDrags(dx, dy, pointerId);
     if (mDragState == STATE_DRAGGING) {
      // Callback might have started an edge drag
      break;
     }

     if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {
      break;
     }
    }
    saveLastMotion(ev);
    break;
   }

   case MotionEventCompat.ACTION_POINTER_UP: {
    final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
    clearMotionHistory(pointerId);
    break;
   }

   case MotionEvent.ACTION_UP:
   case MotionEvent.ACTION_CANCEL: {
    cancel();
    break;
   }
   default:
    break;
  }

  return mDragState == STATE_DRAGGING;
 }

 public void processTouchEvent(MotionEvent ev) {
  final int action = MotionEventCompat.getActionMasked(ev);
  final int actionIndex = MotionEventCompat.getActionIndex(ev);

  if (action == MotionEvent.ACTION_DOWN) {
   // Reset things for a new event stream, just in case we didn't get
   // the whole previous stream.
   cancel();
  }

  if (mVelocityTracker == null) {
   mVelocityTracker = VelocityTracker.obtain();
  }
  mVelocityTracker.addMovement(ev);

  switch (action) {
   case MotionEvent.ACTION_DOWN: {
    final float x = ev.getX();
    final float y = ev.getY();
    final int pointerId = MotionEventCompat.getPointerId(ev, 0);
    final View toCapture = findTopChildUnder((int) x, (int) y);

    saveInitialMotion(x, y, pointerId);

    // Since the parent is already directly processing this touch event,
    // there is no reason to delay for a slop before dragging.
    // Start immediately if possible.
    tryCaptureViewForDrag(toCapture, pointerId);

    final int edgesTouched = mInitialEdgesTouched[pointerId];
    if ((edgesTouched & mTrackingEdges) != 0) {
     mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
    }
    break;
   }

   case MotionEventCompat.ACTION_POINTER_DOWN: {
    final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
    final float x = MotionEventCompat.getX(ev, actionIndex);
    final float y = MotionEventCompat.getY(ev, actionIndex);

    saveInitialMotion(x, y, pointerId);

    // A ViewDragHelper can only manipulate one view at a time.
    if (mDragState == STATE_IDLE) {
     // If we're idle we can do anything! Treat it like a normal down event.

     final View toCapture = findTopChildUnder((int) x, (int) y);
     tryCaptureViewForDrag(toCapture, pointerId);

     final int edgesTouched = mInitialEdgesTouched[pointerId];
     if ((edgesTouched & mTrackingEdges) != 0) {
      mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
     }
    } else if (isCapturedViewUnder((int) x, (int) y)) {
     // We're still tracking a captured view. If the same view is under this
     // point, we'll swap to controlling it with this pointer instead.
     // (This will still work if we're "catching" a settling view.)

     tryCaptureViewForDrag(mCapturedView, pointerId);
    }
    break;
   }

   case MotionEvent.ACTION_MOVE: {
    if (mDragState == STATE_DRAGGING) {
     final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
     final float x = MotionEventCompat.getX(ev, index);
     final float y = MotionEventCompat.getY(ev, index);
     final int idx = (int) (x - mLastMotionX[mActivePointerId]);
     final int idy = (int) (y - mLastMotionY[mActivePointerId]);

     dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);

     saveLastMotion(ev);
    } else {
     // Check to see if any pointer is now over a draggable view.
     final int pointerCount = MotionEventCompat.getPointerCount(ev);
     for (int i = 0; i < pointerCount; i++) {
      final int pointerId = MotionEventCompat.getPointerId(ev, i);
      final float x = MotionEventCompat.getX(ev, i);
      final float y = MotionEventCompat.getY(ev, i);
      final float dx = x - mInitialMotionX[pointerId];
      final float dy = y - mInitialMotionY[pointerId];

      reportNewEdgeDrags(dx, dy, pointerId);
      if (mDragState == STATE_DRAGGING) {
       // Callback might have started an edge drag.
       break;
      }

      final View toCapture = findTopChildUnder((int) x, (int) y);
      if (checkTouchSlop(toCapture, dx, dy) &&
        tryCaptureViewForDrag(toCapture, pointerId)) {
       break;
      }
     }
     saveLastMotion(ev);
    }
    break;
   }

   case MotionEventCompat.ACTION_POINTER_UP: {
    final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
    if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
     // Try to find another pointer that's still holding on to the captured view.
     int newActivePointer = INVALID_POINTER;
     final int pointerCount = MotionEventCompat.getPointerCount(ev);
     for (int i = 0; i < pointerCount; i++) {
      final int id = MotionEventCompat.getPointerId(ev, i);
      if (id == mActivePointerId) {
       // This one's going away, skip.
       continue;
      }

      final float x = MotionEventCompat.getX(ev, i);
      final float y = MotionEventCompat.getY(ev, i);
      if (findTopChildUnder((int) x, (int) y) == mCapturedView &&
        tryCaptureViewForDrag(mCapturedView, id)) {
       newActivePointer = mActivePointerId;
       break;
      }
     }

     if (newActivePointer == INVALID_POINTER) {
      // We didn't find another pointer still touching the view, release it.
      releaseViewForPointerUp();
     }
    }
    clearMotionHistory(pointerId);
    break;
   }

   case MotionEvent.ACTION_UP: {
    if (mDragState == STATE_DRAGGING) {
     releaseViewForPointerUp();
    }
    cancel();
    break;
   }

   case MotionEvent.ACTION_CANCEL: {
    if (mDragState == STATE_DRAGGING) {
     dispatchViewReleased(0, 0);
    }
    cancel();
    break;
   }
   default:
    break;

  }
 }

 private void reportNewEdgeDrags(float dx, float dy, int pointerId) {
  int dragsStarted = 0;
  if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {
   dragsStarted |= EDGE_LEFT;
  }
  if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {
   dragsStarted |= EDGE_TOP;
  }
  if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {
   dragsStarted |= EDGE_RIGHT;
  }
  if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {
   dragsStarted |= EDGE_BOTTOM;
  }

  if (dragsStarted != 0) {
   mEdgeDragsInProgress[pointerId] |= dragsStarted;
   mCallback.onEdgeDragStarted(dragsStarted, pointerId);
  }
 }

 private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) {
  final float absDelta = Math.abs(delta);
  final float absODelta = Math.abs(odelta);

  if ((mInitialEdgesTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0 ||
    (mEdgeDragsLocked[pointerId] & edge) == edge ||
    (mEdgeDragsInProgress[pointerId] & edge) == edge ||
    (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {
   return false;
  }
  if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {
   mEdgeDragsLocked[pointerId] |= edge;
   return false;
  }
  return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop;
 }


 private boolean checkTouchSlop(View child, float dx, float dy) {
  if (child == null) {
   return false;
  }
  final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0;
  final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;

  if (checkHorizontal && checkVertical) {
   return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
  } else if (checkHorizontal) {
   return Math.abs(dx) > mTouchSlop;
  } else if (checkVertical) {
   return Math.abs(dy) > mTouchSlop;
  }
  return false;
 }

 public boolean checkTouchSlop(int directions) {
  final int count = mInitialMotionX.length;
  for (int i = 0; i < count; i++) {
   if (checkTouchSlop(directions, i)) {
    return true;
   }
  }
  return false;
 }


 public boolean checkTouchSlop(int directions, int pointerId) {
  if (!isPointerDown(pointerId)) {
   return false;
  }

  final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL;
  final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL;

  final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId];
  final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId];

  if (checkHorizontal && checkVertical) {
   return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
  } else if (checkHorizontal) {
   return Math.abs(dx) > mTouchSlop;
  } else if (checkVertical) {
   return Math.abs(dy) > mTouchSlop;
  }
  return false;
 }

 public boolean isEdgeTouched(int edges) {
  final int count = mInitialEdgesTouched.length;
  for (int i = 0; i < count; i++) {
   if (isEdgeTouched(edges, i)) {
    return true;
   }
  }
  return false;
 }


 public boolean isEdgeTouched(int edges, int pointerId) {
  return isPointerDown(pointerId) && (mInitialEdgesTouched[pointerId] & edges) != 0;
 }

 private void releaseViewForPointerUp() {
  mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
  final float xvel = clampMag(
    VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
    mMinVelocity, mMaxVelocity);
  final float yvel = clampMag(
    VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
    mMinVelocity, mMaxVelocity);
  dispatchViewReleased(xvel, yvel);
 }

 private void dragTo(int left, int top, int dx, int dy) {
  int clampedX = left;
  int clampedY = top;
  final int oldLeft = mCapturedView.getLeft();
  final int oldTop = mCapturedView.getTop();
  if (dx != 0) {
   clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
   mCapturedView.offsetLeftAndRight(clampedX - oldLeft);
  }
  if (dy != 0) {
   clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
   mCapturedView.offsetTopAndBottom(clampedY - oldTop);
  }

  if (dx != 0 || dy != 0) {
   final int clampedDx = clampedX - oldLeft;
   final int clampedDy = clampedY - oldTop;
   mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,
     clampedDx, clampedDy);
  }
 }


 public boolean isCapturedViewUnder(int x, int y) {
  return isViewUnder(mCapturedView, x, y);
 }

 public boolean isViewUnder(View view, int x, int y) {
  if (view == null) {
   return false;
  }
  return x >= view.getLeft() &&
    x < view.getRight() &&
    y >= view.getTop() &&
    y < view.getBottom();
 }

 public View findTopChildUnder(int x, int y) {
  final int childCount = mParentView.getChildCount();
  for (int i = childCount - 1; i >= 0; i--) {
   final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
   if (x >= child.getLeft() && x < child.getRight() &&
     y >= child.getTop() && y < child.getBottom()) {
    return child;
   }
  }
  return null;
 }

 private int getEdgesTouched(int x, int y) {
  int result = 0;

  if (x < mParentView.getLeft() + mEdgeSize) result |= EDGE_LEFT;
  if (y < mParentView.getTop() + mEdgeSize) result |= EDGE_TOP;
  if (x > mParentView.getRight() - mEdgeSize) result |= EDGE_RIGHT;
  if (y > mParentView.getBottom() - mEdgeSize) result |= EDGE_BOTTOM;

  return result;
 }
}

DragLayout布局繼承FrameLayout:

public class DragLayout extends FrameLayout {

 private static final boolean IS_SHOW_SHADOW = true;
 //手勢處理類
 private GestureDetectorCompat gestureDetector;
 //視圖拖拽移動幫助類
 private ViewDragHelper dragHelper;
 //滑動監聽器
 private DragListener dragListener;
 //水平拖拽的距離
 private int range;
 //寬度
 private int width;
 //高度
 private int height;
 //main視圖距離在ViewGroup距離左邊的距離
 private int mainLeft;
 private Context context;
 private ImageView ivShadow;
 //左側布局
 private RelativeLayout vgLeft;
 //右側(主界面布局)
 private CustomRelativeLayout vgMain;
 //頁面狀態 默認為關閉
 private Status status = Status.CLOSE;


 private final ViewDragHelper.Callback dragHelperCallback = new ViewDragHelper.Callback() {

  @Override
  public int clampViewPositionHorizontal(View child, int left, int dx) {
   if (mainLeft + dx < 0) {
    return 0;
   } else if (mainLeft + dx > range) {
    return range;
   } else {
    return left;
   }
  }

  @Override
  public boolean tryCaptureView(View child, int pointerId) {
   return true;
  }

  @Override
  public int getViewHorizontalDragRange(View child) {
   return width;
  }


  @Override
  public void onViewReleased(View releasedChild, float xvel, float yvel) {
   super.onViewReleased(releasedChild, xvel, yvel);
   if (xvel > 0) {
    open();
   } else if (xvel < 0) {
    close();
   } else if (releasedChild == vgMain && mainLeft > range * 0.3) {
    open();
   } else if (releasedChild == vgLeft && mainLeft > range * 0.7) {
    open();
   } else {
    close();
   }
  }

  @Override
  public void onViewPositionChanged(View changedView, int left, int top,
           int dx, int dy) {
   if (changedView == vgMain) {
    mainLeft = left;
   } else {
    mainLeft = mainLeft + left;
   }
   if (mainLeft < 0) {
    mainLeft = 0;
   } else if (mainLeft > range) {
    mainLeft = range;
   }

   if (IS_SHOW_SHADOW) {
    ivShadow.layout(mainLeft, 0, mainLeft + width, height);
   }
   if (changedView == vgLeft) {
    vgLeft.layout(0, 0, width, height);
    vgMain.layout(mainLeft, 0, mainLeft + width, height);
   }

   dispatchDragEvent(mainLeft);
  }
 };

 public DragLayout(Context context) {
  this(context, null);
 }

 public DragLayout(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
  this.context = context;
 }

 public DragLayout(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  gestureDetector = new GestureDetectorCompat(context, new YScrollDetector());
  dragHelper = ViewDragHelper.create(this, dragHelperCallback);
 }

 class YScrollDetector extends GestureDetector.SimpleOnGestureListener {
  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx, float dy) {
   return Math.abs(dy) <= Math.abs(dx);
  }
 }

 /**
  * 滑動相關回調接口
  */
 public interface DragListener {
  //界面打開
  public void onOpen();
  //界面關閉
  public void onClose();
  //界面滑動過程中
  public void onDrag(float percent);
 }
 public void setDragListener(DragListener dragListener) {
  this.dragListener = dragListener;
 }

 /**
  * 布局加載完成回調
  * 做一些初始化的操作
  */
 @Override
 protected void onFinishInflate() {
  super.onFinishInflate();
  if (IS_SHOW_SHADOW) {
   ivShadow = new ImageView(context);
   ivShadow.setImageResource(R.mipmap.shadow);
   LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
   addView(ivShadow, 1, lp);
  }
  //左側界面
  vgLeft = (RelativeLayout) getChildAt(0);
  //右側(主)界面
  vgMain = (CustomRelativeLayout) getChildAt(IS_SHOW_SHADOW ? 2 : 1);
  vgMain.setDragLayout(this);
  vgLeft.setClickable(true);
  vgMain.setClickable(true);
 }

 public ViewGroup getVgMain() {
  return vgMain;
 }

 public ViewGroup getVgLeft() {
  return vgLeft;
 }

 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  width = vgLeft.getMeasuredWidth();
  height = vgLeft.getMeasuredHeight();
  //可以水平拖拽滑動的距離 一共為屏幕寬度的80%
  range = (int) (width * 0.8f);
 }


 @Override
 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  vgLeft.layout(0, 0, width, height);
  vgMain.layout(mainLeft, 0, mainLeft + width, height);
 }

 /**
  * 攔截觸摸事件
  * @param ev
  * @return
  */
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  return dragHelper.shouldInterceptTouchEvent(ev) && gestureDetector.onTouchEvent(ev);
 }

 /**
  * 將攔截的到事件給ViewDragHelper進行處理
  * @param e
  * @return
  */
 @Override
 public boolean onTouchEvent(MotionEvent e) {
  try {
   dragHelper.processTouchEvent(e);
  } catch (Exception ex) {
   ex.printStackTrace();
  }
  return false;
 }

 /**
  * 進行處理拖拽事件
  * @param mainLeft
  */
 private void dispatchDragEvent(int mainLeft) {
  if (dragListener == null) {
   return;
  }
  float percent = mainLeft / (float) range;
  //滑動動畫效果
  animateView(percent);
  //進行回調滑動的百分比
  dragListener.onDrag(percent);
  Status lastStatus = status;
  if (lastStatus != getStatus() && status == Status.CLOSE) {
   dragListener.onClose();
  } else if (lastStatus != getStatus() && status == Status.OPEN) {
   dragListener.onOpen();
  }
 }

 /**
  * 根據滑動的距離的比例,進行平移動畫
  * @param percent
  */
 private void animateView(float percent) {
  float f1 = 1 - percent * 0.5f;

  ViewHelper.setTranslationX(vgLeft, -vgLeft.getWidth() / 2.5f + vgLeft.getWidth() / 2.5f * percent);
  if (IS_SHOW_SHADOW) {
   //陰影效果視圖大小進行縮放
   ViewHelper.setScaleX(ivShadow, f1 * 1.2f * (1 - percent * 0.10f));
   ViewHelper.setScaleY(ivShadow, f1 * 1.85f * (1 - percent * 0.10f));
  }
 }
 /**
  * 有加速度,當我們停止滑動的時候,該不會立即停止動畫效果
  */
 @Override
 public void computeScroll() {
  if (dragHelper.continueSettling(true)) {
   ViewCompat.postInvalidateOnAnimation(this);
  }
 }

 /**
  * 頁面狀態(滑動,打開,關閉)
  */
 public enum Status {
  DRAG, OPEN, CLOSE
 }

 /**
  * 頁面狀態設置
  * @return
  */
 public Status getStatus() {
  if (mainLeft == 0) {
   status = Status.CLOSE;
  } else if (mainLeft == range) {
   status = Status.OPEN;
  } else {
   status = Status.DRAG;
  }
  return status;
 }

 public void open() {
  open(true);
 }

 public void open(boolean animate) {
  if (animate) {
   //繼續滑動
   if (dragHelper.smoothSlideViewTo(vgMain, range, 0)) {
    ViewCompat.postInvalidateOnAnimation(this);
   }
  } else {
   vgMain.layout(range, 0, range * 2, height);
   dispatchDragEvent(range);
  }
 }

 public void close() {
  close(true);
 }

 public void close(boolean animate) {
  if (animate) {
   //繼續滑動
   if (dragHelper.smoothSlideViewTo(vgMain, 0, 0)) {
    ViewCompat.postInvalidateOnAnimation(this);
   }
  } else {
   vgMain.layout(0, 0, width, height);
   dispatchDragEvent(0);
  }
 }
}

CustomRelativeLayout:

public class CustomRelativeLayout extends RelativeLayout {
 private DragLayout dl;
 public CustomRelativeLayout(Context context) {
  super(context);
 }

 public CustomRelativeLayout(Context context, AttributeSet attrs) {
  super(context, attrs);
 }

 public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
 }

 public void setDragLayout(DragLayout dl) {
  this.dl = dl;
 }

 @Override
 public boolean onInterceptTouchEvent(MotionEvent event) {
  if (dl.getStatus() != DragLayout.Status.CLOSE) {
   return true;
  }
  return super.onInterceptTouchEvent(event);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (dl.getStatus() != DragLayout.Status.CLOSE) {
   if (event.getAction() == MotionEvent.ACTION_UP) {
    dl.close();
   }
   return true;
  }
  return super.onTouchEvent(event);
 }

}

c沉浸式狀態欄:BaseActivity:

public class BaseActivity extends FragmentActivity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   //透明狀態欄
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
   //透明導航欄
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
  }
 }
 /**
  * 設置沉浸式狀態欄
  */
 protected void setStatusBar() {
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
   final ViewGroup linear_bar = (ViewGroup) findViewById(R.id.rl_title);
   final int statusHeight = getStatusBarHeight();
   linear_bar.post(new Runnable() {
    @Override
    public void run() {
     int titleHeight = linear_bar.getHeight();
     android.widget.LinearLayout.LayoutParams params = (android.widget.LinearLayout.LayoutParams) linear_bar.getLayoutParams();
     params.height = statusHeight + titleHeight;
     linear_bar.setLayoutParams(params);
    }
   });
  }
 }
 /**
  * 獲取狀態欄的高度
  * @return
  */
 protected int getStatusBarHeight(){
  try
  {
   Class<?> c=Class.forName("com.android.internal.R$dimen");
   Object obj=c.newInstance();
   Field field=c.getField("status_bar_height");
   int x=Integer.parseInt(field.get(obj).toString());
   return getResources().getDimensionPixelSize(x);
  }catch(Exception e){
   e.printStackTrace();
  }
  return 0;
 }

}

MainActivity:

public class MainActivity extends BaseActivity {
 private DragLayout dl;
 private LinearLayout linearLayout;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  initDragLayout();
 }
 private void initDragLayout() {
  dl = (DragLayout) findViewById(R.id.dl);
  linearLayout =(LinearLayout) findViewById(R.id.aaa);
  dl.setDragListener(new DragLayout.DragListener() {
   //界面打開的時候
   @Override
   public void onOpen() {
   }
   //界面關閉的時候
   @Override
   public void onClose() {
   }

   //界面滑動的時候
   @Override
   public void onDrag(float percent) {
   }
  });
 }
}

OneFragment:

public class OneFragment extends Fragment {
 private View mView;
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  if(mView==null){
   mView=inflater.inflate(R.layout.one_frag_layout,container,false);
  }
  return mView;
 }
}

b布局文件:
one_frag_layout.xml
LinearLayout布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center"
 android:id="@+id/aaa"

 >

 <TextView
  android:text="界面"
  android:layout_gravity="center"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content" />
</LinearLayout>

one_frag_layout1.xml:
RelativeLayout布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center"
 >

 <TextView
  android:text="主界面"
  android:layout_gravity="center"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content" />
</RelativeLayout>

activity.main.xml:

<com.包名.myapplication.widget.DragLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/dl"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@android:color/transparent"
 >
 <!--下層 左邊的布局 這個布局不能省去-->
 <include layout="@layout/one_frag_layout1"/>
 <!--上層 右邊的主布局-->
 <com.包名.myapplication.widget.CustomRelativeLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#FFFFFF"
  >
  <LinearLayout
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical"
   >
  <RelativeLayout
   android:id="@+id/rl_title"
   android:layout_width="match_parent"
   android:layout_height="49dp"
   android:gravity="bottom"
   android:background="#2aaced"
    >
   <include layout="@layout/one_frag_layout"/>
  </RelativeLayout>
  <!--中間內容後面放入Fragment-->
  <FrameLayout
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
   <fragment
    android:id="@+id/main_info_fragment"
    class="com.mieasy.myapplication.OneFragment"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"/>
  </FrameLayout>
  </LinearLayout>
 </com.包名.myapplication.widget.CustomRelativeLayout>
</com.包名.myapplication.widget.DragLayout>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved