Android onScroll事件给出了错误的值

时间:2014-05-23 08:12:02

标签: java android android-canvas

我尝试使用Android画布和GuestureDetector.SimpleOnGestureListener实现自定义双向滚动,但我遇到了一些问题。它似乎是第一个滚动事件,它总是给出一个巨大的不准确的跳跃。

例如,如果我单击画布中间并滚动一点点,我会看到像这样的滚动事件(稍微圆化):

scroll x: -352 scroll y: -373
scroll x: -4 scroll y: 3
scroll x -4 scroll y: 3

滚动的第一个值总是一个巨大的跳跃,我没有用我的手指远程执行我滚动。好像我把手指从画布的一角移到手指实际上的位置似乎是在考虑我的第一个滚动动作?

这是我的实际聆听者:

public class BoardScrollListener extends GestureDetector.SimpleOnGestureListener {

    private GameService gameService = GameService.getInstance();
    private UiService uiService = UiService.getInstance();

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        Log.d("scroll", "scroll x: " + distanceX + " scroll y: " + distanceY);
        if (distanceX > -150 && distanceY > -150) {
            Game game = gameService.getGame();
            game.setxPixelOffset((int) (game.getxPixelOffset() - distanceX));
            game.setyPixelOffset((int) (game.getyPixelOffset() - distanceY));

            uiService.getGameboardActivity().getGameboard().invalidate();
        }
        return true;
    }
    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }
}

我尝试将其包装在那里,如果检查发生了什么,并且实际上大部分时间都解决了问题,但它显然不是一个真正的解决方案。任何人都可以告诉我为什么运动事件是不准确的,并且是一种使其准确或忽略滚动的第一个运动事件的好方法吗?

我正在使用v4支持活动片段,因此我也尝试切换到GestureDetectorCompat,但这并没有改变任何内容。 (这是在KitKat设备上。)

4 个答案:

答案 0 :(得分:2)

首先,您获得ACTION_DOWN事件的坐标,然后获得更晚的距离。我认为你应该检查一下事件的行动。

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

    Log.d("scroll", "scroll x: " + distanceX + " scroll y: " + distanceY);

    // ! CHECK FOR EVENT ACTION !
    if (e2.getAction() == MotionEvent.ACTION_MOVE) {
        Game game = gameService.getGame();
        game.setxPixelOffset((int) (game.getxPixelOffset() - distanceX));
        game.setyPixelOffset((int) (game.getyPixelOffset() - distanceY));

        uiService.getGameboardActivity().getGameboard().invalidate();
    }
    return true;
}

答案 1 :(得分:1)

我遇到了同样的问题。在我的情况下,我发现这是因为我使用了onInterceptTouchEvent()。我需要这个,因为ACTION_DOWN事件应由我的viewgroup的子进程处理。但是,当拦截ACTION_MOVE事件并将其提供给GestureDetector时,ACTION_MOVE之前的MOTION_DOWN事件不会被发送。在GestureListener的onScroll()中,e1始终为null。

在onScroll()中,第一个distanceX是根据e1的positionX计算的。之后使用前一个e2的positionX。如果缺少onScroll()使用0或前一个滚动发生ACTION_UP的位置,如果存在。

无论如何,长话短说。为了使它工作,我决定忽略onScroll()中的第一个ACTION_MOVE。请参阅下面的我使用的代码。

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {

    final int action = MotionEventCompat.getActionMasked(event);

    // always handel touch event completion
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
        //Log.i("Intercept Up: ", "END GESTURE");
        mIsScrolling = false;
        return false;
    }

    switch (action) {
        case MotionEvent.ACTION_DOWN: {
            // Record position
            mDownX = event.getX();
            // let the child handel the event
            return false;
        }
        case MotionEvent.ACTION_MOVE: {
            if (mIsScrolling) {
                // we are currently scrolling, so intercept the event
                return true;
            }
            float dx = event.getX() - mDownX;
            if (Math.abs(dx) > mTouchSlop) {
                // We met the motion threshold; Intercept the event to initiate scrolling
                mIsScrolling = true;
                return true;
            }
            // Not scrolling and threshold not met
            return false;
        }
        default: {
            // other motion events will not be intercepted
            return false;
        }
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    final int action = MotionEventCompat.getActionMasked(event);

    // always handel touch event completion
    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
        mIsScrolling = false;
        // Used to ignore the first scroll event with bad distanceX
        mIsFirstScrollEvent = true;
        return true;
    }
    // Use GestureDetector to detect event type
    mGestureDetector.onTouchEvent(event);
    return true;
}


private final GestureDetector.OnGestureListener mGestureListener =
        new GestureDetector.SimpleOnGestureListener() {

    @Override
    public boolean onScroll (MotionEvent e1, MotionEvent e2,
                             float distanceX, float distanceY) {

        // First scroll event should be ignored because of bad distanceX
        if (mIsFirstScrollEvent) {
            mIsFirstScrollEvent = false;
            return true;
        }

        // Do the scroll stuff here

        return true;
    }
};

答案 2 :(得分:0)

我单独附加导致此问题的onTouch侦听器。将该事件的正文拉入我的自定义视图onTouchEvent重写的侦听器修复了该问题。

答案 3 :(得分:0)

我有同样的问题。

但在我的情况下,我对onInterceptTouchEvent(...)和onTouchEvent(...)方法使用不同的OnGestureListener-。

因此我只是在拦截侦听器中设置一个标志,忽略传递给实际触摸事件监听器的第一个滚动事件

private class GestureInterceptListener extends GestureDetector.SimpleOnGestureListener {

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        final boolean shouldIntercept = ...; // here we decide should we intercept this event, or should it be passed further 
        if (shouldIntercept) {
            mGestureProcessListener.setFirstScrollEventIgnored(true);
        }
        return shouldIntercept;
    }

}


private class GestureProcessListener extends GestureDetector.SimpleOnGestureListener {

    private boolean ignoreFirstBuggyScrollEvent = false;

    /**
     * For some reason first scroll event on half-visible settings list gets strange incorrect negative values
     * @param isIgnored - should next scroll event be ignored? value resets to false afterwards
     */
    public void setFirstScrollEventIgnored(boolean isIgnored) {
        ignoreFirstBuggyScrollEvent = isIgnored;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        if (ignoreFirstBuggyScrollEvent) {
            ignoreFirstBuggyScrollEvent = false;
            return true;
        }
        ... // process scroll event here
        return true;
    }

}

为我工作