Android onScroll事件提供了错误的值

3
我正在尝试使用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;
    }
}

我尝试使用if语句进行包装以查看问题发生了什么,这通常可以解决问题,但显然并不是真正的解决方法。有人能告诉我为什么运动事件不准确,并且有一种好的方法使其准确或忽略滚动的第一个运动事件吗?

我正在使用v4支持活动片段,所以我也尝试切换到GestureDetectorCompat,但这没有改变任何东西。(在KitKat设备上)


重复问题:https://dev59.com/cWjWa4cB1Zd3GeqProyK - Siva
4个回答

3
我遇到了同样的问题。在我的情况下,我发现这是由于我正在使用 onInterceptTouchEvent()。我需要这个函数,因为 ACTION_DOWN 事件应该由我的 viewgroup 的子项处理。然而,在拦截 ACTION_MOVE 事件并将其传递给 GestureDetector 时,前面的 MOTION_DOWN 事件不会被发送。在 GestureListener 的 onScroll() 中,e1 始终为 null。
在 onScroll() 中,第一个 distanceX 是基于 e1 的 positionX 计算的。之后使用上一个 e2 的 positionX。如果 e1 缺失,则 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

首先,您需要获取 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;
}

我在日志行中添加了一个消息,用于判断e2是否为ACTION_MOVE,并且它始终是ACTION_MOVE。我觉得我的问题一定与将此GestureListener附加到自定义视图画布有关? - CorayThan
我不确定,但是在onScroll的第一次调用中会得到这些大的值,因为e1是ACTION_DOWN滚动事件的起始点。 - kamil zych
e1始终为空。在所有情况下,这两个事件似乎都是相同的。我只看到距离发生变化。 - CorayThan
请查看http://developer.android.com/reference/android/view/GestureDetector.OnGestureListener.html中的onScroll方法 - 其中指出e1是按下事件,而e2是滚动事件。 - kamil zych
1
我曾经将一个普通的“onTouch”监听器附加到自定义视图上。似乎这样会搞砸一切。将该监听器放入onTouchEvent中,并始终从onTouchEvent返回true似乎已经解决了问题。 - CorayThan

1
我当时单独添加了一个 onTouch 监听器,导致了这个问题。将该事件的主体放入我的自定义视图的 onTouchEvent 覆盖监听器中解决了这个问题。

0

我有同样的问题。

但我的情况是,我为onInterceptTouchEvent(...)和onTouchEvent(...)方法使用了不同的OnGestureListener-s。

因此,我只是在intercept监听器中设置一个标志,用于忽略传递给实际touch事件监听器的第一个滚动事件。

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;
    }

}

对我有效


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接