@Override
  public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
      // This is called at drawing time by ViewGroup. We don't want to
      // re-show the scrollbars at this point, which scrollTo will do,
      // so we replicate most of scrollTo here.
      //
      // It's a little odd to call onScrollChanged from inside the drawing.
      //
      // It is, except when you remember that computeScroll() is used to
      // animate scrolling. So unless we want to defer the onScrollChanged()
      // until the end of the animated scrolling, we don't really have a
      // choice here.
      //
      // I agree. The alternative, which I think would be worse, is to post
      // something and tell the subclasses later. This is bad because there
      // will be a window where mScrollX/Y is different from what the app
      // thinks it is.
      //
      int oldX = getScrollX();
      int oldY = getScrollY();
      int x = mScroller.getCurrX();
      int y = mScroller.getCurrY();

      if (oldX != x || oldY != y) {
        final int range = getScrollRange();
        final int overscrollMode = getOverScrollMode();
        final boolean canOverscroll =
            overscrollMode == OVER_SCROLL_ALWAYS
                || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

        overScrollBy(x - oldX, y - oldY, oldX, oldY, range, 0, mOverflingDistance, 0, false);
        onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
      }

      if (!awakenScrollBars()) {
        postInvalidateOnAnimation();
      }
    } else {
      int scrollX = getScrollX();
      int finalX = getScrollXFromFinalX(scrollX);

      if (!mIsBeingDragged && scrollX != finalX) {
        if (mScroller.springBack(getScrollX(), getScrollY(), finalX, finalX, 0, 0)) {
          postInvalidateOnAnimation();
        }
      }
    }
  }
  private boolean touchEvent(MotionEvent ev) {
    initVelocityTrackerIfNotExists();
    mVelocityTracker.addMovement(ev);

    final int action = ev.getAction();

    switch (action & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        {
          if (getChildCount() == 0 || !inScrollArea(ev)) {
            return false;
          }
          if ((mIsBeingDragged = !mScroller.isFinished())) {
            final ViewParent parent = getParent();
            if (parent != null) {
              parent.requestDisallowInterceptTouchEvent(true);
            }
          }

          /*
           * If being flinged and user touches, stop the fling. isFinished will be false
           * if being flinged.
           */
          if (!mScroller.isFinished()) {
            mScroller.abortAnimation();
          }

          // Remember where the motion event started
          mLastMotionX = (int) ev.getX();
          mActivePointerId = ev.getPointerId(0);
          break;
        }
      case MotionEvent.ACTION_MOVE:
        final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
        if (activePointerIndex == -1) {
          Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
          break;
        }

        final int x = (int) ev.getX(activePointerIndex);
        int deltaX = mLastMotionX - x;
        if (!mIsBeingDragged && Math.abs(deltaX) > mTouchSlop) {
          final ViewParent parent = getParent();
          if (parent != null) {
            parent.requestDisallowInterceptTouchEvent(true);
          }
          mIsBeingDragged = true;
          if (deltaX > 0) {
            deltaX -= mTouchSlop;
          } else {
            deltaX += mTouchSlop;
          }
        }
        if (mIsBeingDragged) {
          // Scroll to follow the motion event
          mLastMotionX = x;

          final int oldX = getScrollX();
          final int oldY = getScrollY();
          final int range = getScrollRange();
          final int overscrollMode = getOverScrollMode();
          final boolean canOverscroll =
              overscrollMode == OVER_SCROLL_ALWAYS
                  || (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

          if (overScrollBy(deltaX, 0, getScrollX(), 0, range, 0, mOverscrollDistance, 0, true)) {
            // Break our velocity if we hit a scroll barrier.
            mVelocityTracker.clear();
          }
          onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mIsBeingDragged) {
          final VelocityTracker velocityTracker = mVelocityTracker;
          velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
          int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);

          if (getChildCount() > 0) {
            if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
              fling(-initialVelocity);
            } else {
              int finalX = getScrollX();
              finalX = getScrollXFromFinalX(finalX);
              if (mScroller.springBack(getScrollX(), getScrollY(), finalX, finalX, 0, 0)) {
                postInvalidateOnAnimation();
              }
            }
          }

          mActivePointerId = INVALID_POINTER;
          mIsBeingDragged = false;
          recycleVelocityTracker();
        }
        break;
      case MotionEvent.ACTION_CANCEL:
        if (mIsBeingDragged && getChildCount() > 0) {
          if (mScroller.springBack(getScrollX(), getScrollY(), 0, getScrollRange(), 0, 0)) {
            postInvalidateOnAnimation();
          }
          mActivePointerId = INVALID_POINTER;
          mIsBeingDragged = false;
          recycleVelocityTracker();
        }
        break;
      case MotionEvent.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        break;
    }
    return true;
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // return super.onTouchEvent(event);

    // int height = getHeight();

    initVelocityTrackerIfNotExists();
    mVelocityTracker.addMovement(event);

    int action = event.getActionMasked();

    if (action == MotionEvent.ACTION_DOWN) {
      if (!mIsDown) {
        mIsDown = true;
        mDownPt.x = event.getX();
        mDownPt.y = event.getY();
        mDownedScrollPt.x = getScrollX();
        mDownedScrollPt.y = getScrollY();
        // initOrResetVelocityTracker();
        // mVelocityTracker.addMovement(event);

        if (!mScroller.isFinished()) {
          mScroller.abortAnimation();
        }
      }
    } else if (action == MotionEvent.ACTION_MOVE) {
      if (mIsDown) {
        float x = event.getX();
        float y = event.getY();
        float sx = -(x - mDownPt.x) + mDownedScrollPt.x;
        float sy = -(y - mDownPt.y) + mDownedScrollPt.y;
        scrollTo((int) sx, (int) sy);
        //                initVelocityTrackerIfNotExists();
        //                mVelocityTracker.addMovement(event);
      }
    } else if (action == MotionEvent.ACTION_UP) {
      if (mIsDown) {

        final VelocityTracker velocityTracker = mVelocityTracker;
        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
        int initialVelocity = (int) velocityTracker.getYVelocity(event.getPointerId(0));
        int initialVelocitx = (int) velocityTracker.getXVelocity(event.getPointerId(0));
        fling(-initialVelocitx, -initialVelocity);

        recycleVelocityTracker();
        mIsDown = false;
      }
    } else if (action == MotionEvent.ACTION_CANCEL) {
      if (mIsDown) {
        if (mScroller.springBack(
            getScrollX(), getScrollY(), 0, getScrollRangeX(), 0, getScrollRangeY())) {
          // postInvalidateOnAnimation();
          postInvalidate();
        }
        recycleVelocityTracker();
        mIsDown = false;
      }
    }

    return true;
  }
  private boolean interceptTouchEvent(MotionEvent ev) {
    /*
     * This method JUST determines whether we want to intercept the motion. If we
     * return true, onMotionEvent will be called and we do the actual scrolling there.
     */

    /*
     * Shortcut the most recurring case: the user is in the dragging state and he is
     * moving his finger. We want to intercept this motion.
     */
    final int action = ev.getAction();
    if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
      return true;
    }

    switch (action & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_MOVE:
        {
          /*
           * mIsBeingDragged == false, otherwise the shortcut would have caught it.
           * Check whether the user has moved far enough from his original down touch.
           */

          /*
           * Locally do absolute value. mLastMotionX is set to the x value of the down
           * event.
           */
          final int activePointerId = mActivePointerId;
          if (activePointerId == INVALID_POINTER) {
            // If we don't have a valid id, the touch down wasn't on content.
            break;
          }

          final int pointerIndex = ev.findPointerIndex(activePointerId);
          if (pointerIndex == -1) {
            Log.e(TAG, "Invalid pointerId=" + activePointerId + " in onInterceptTouchEvent");
            break;
          }

          final int x = (int) ev.getX(pointerIndex);
          final int xDiff = (int) Math.abs(x - mLastMotionX);
          if (xDiff > mTouchSlop) {
            setHorizontalScrollBarEnabled(true);
            mIsBeingDragged = true;
            mLastMotionX = x;
            initVelocityTrackerIfNotExists();
            mVelocityTracker.addMovement(ev);
            if (getParent() != null) getParent().requestDisallowInterceptTouchEvent(true);
          }
          break;
        }

      case MotionEvent.ACTION_DOWN:
        {
          final int x = (int) ev.getX();
          if (!inChild((int) x, (int) ev.getY())) {
            mIsBeingDragged = false;
            recycleVelocityTracker();
            break;
          }

          /*
           * Remember location of down touch. ACTION_DOWN always refers to pointer index
           * 0.
           */
          mLastMotionX = x;
          mActivePointerId = ev.getPointerId(0);

          initOrResetVelocityTracker();
          mVelocityTracker.addMovement(ev);

          /*
           * If being flinged and user touches the screen, initiate drag; otherwise
           * don't. mScroller.isFinished should be false when being flinged.
           */
          mIsBeingDragged = false;
          break;
        }

      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        /* Release the drag */
        mIsBeingDragged = false;
        mActivePointerId = INVALID_POINTER;
        if (mScroller.springBack(getScrollX(), getScrollY(), 0, getScrollRange(), 0, 0)) {
          postInvalidateOnAnimation();
        }
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        {
          final int index = ev.getActionIndex();
          mLastMotionX = (int) ev.getX(index);
          mActivePointerId = ev.getPointerId(index);
          break;
        }
      case MotionEvent.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        mLastMotionX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
        break;
    }

    /*
     * The only time we want to intercept motion events is if we are in the drag mode.
     */
    return mIsBeingDragged;
  }
 public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) {
   return mScroller.springBack(startX, startY, minX, maxX, minY, maxY);
 }