15

I'm writing an app that involves writing on the screen using one's finger, or eventually a stylus. I have that part working. On ACTION_DOWN, starts drawing; on ACTION_MOVE, adds line segments; on ACTION_UP, finishes line.

The problem is that after ACTION_DOWN, apparently the pointer needs to move more than 10 pixels away from where it started (basically a 20x20 box around the starting point) in order to begin sending ACTION_MOVE events. After leaving the box, the move events are all quite accurate. (I figured out the 10 pixel thing by testing it.) Since this is meant to be used for writing or drawing, 10 pixels is a fairly significant loss: depending on how small you're trying to write, you can lose the first letter or two. I haven't been able to find anything about it - only a couple posts on a forum or two, like http://android.modaco.com/topic/339694-touch-input-problem-not-detecting-very-small-movements/page_pid_1701028#entry1701028. It seems to be present on some devices or systems and not others. No ideas as to how to get rid of it when you have it, though.

I'm using a Galaxy Tab 10.1, with Android 3.1. I've tried several different things to try to get rid of it: I've tried setting the event's coords to something else to see if I could trick it into thinking the cursor was in a different place; I tried re-dispatching the event with the coords changed (my handler reacted to the new points, but still didn't respond to movements in the 10-pixel radius.) I've searched through source code for any references to the effect, and found none (though I think it's from a different version of Android - code for 3.1 isn't released yet, is it?) I've searched for methods of querying the current state of the pointers, so I could just have a timer catch the changes until the pointer crossed the threshold. Couldn't find any way of getting pointer coords without a corresponding movement event. Nothing worked. Does anybody know anything about this, or have any ideas or work-arounds? Thank you.

-- Update: Drag and drop events show the same threshold.

3 Answers 3

26

I agree in part with the post by @passsy but come to a different conclusion. Firstly as mentioned, the mTouchSlop is the value that we are interested in and is exposed via ViewConfiguration.get(context).getScaledTouchSlop();

If you check the Android source for the ViewConfiguraton, the default value for TOUCH_SLOP is 8dip, but the comments mention that this value is a fallback only, and the actual value is defined when the Android OS for that specific device is built. (it may be more or less than this value. It appears to hold true for the Galaxy Tab devices)

More specific to the code sample, the mTouchSlop value is read from the ViewConfiguration when the View is initialised, but the value is only accessed in the onTouchEvent method. If you extend View and override this method (without calling super) then the behaviour of mTouchSlop in the View class is no longer relevant.

More telling (to us) was that when changing the Android settings to overlay touch events on the screen, a touch with a small drag does not register as a motion event, highlighted by the fact that the crosshairs from the Android OS do not move. From this our conclusion is that the minimal drag distance is being enforced at the OS level and your application will never be aware of drag events smaller than the TOUCH_SLOP value. You should also be aware that TOUCH_SLOP should not be used directly and the API deprecates the getTouchSlop method and recommends getScaledTouchSlop which takes the device screen size and pixel density into account. A side effect of this is that the actual minimum stroke length as perceived on different devices may vary. eg on a Galaxy Tab 2.0 7.0" it feels like we are able to draw shorter minimum strokes using the same code base than when running on a Galaxy Tab 2.0 10.1"

You should also be aware that (if you find a way to alter this value), this value determines how Android systems distinguish between taps and strokes. That is if you tap the screen but your finger moves slightly while performing the tap, it will be interpreted as a tap if it moved less than TOUCH_SLOP, but as a stroke if it moved more than TOUCH_SLOP. Therefore setting TOUCH_SLOP to a smaller value will increase the chance that a tap will be interpreted as a stroke.

Our own conclusion is that this minimum distance is not something that can be changed in practice and is something we need to live with.

4

The problem is on Line 6549 in class View https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/View.java

if (!pointInView(x, y, mTouchSlop)) {...}

 /**
 * Utility method to determine whether the given point, in local coordinates,
 * is inside the view, where the area of the view is expanded by the slop factor.
 * This method is called while processing touch-move events to determine if the event
 * is still within the view.
 */
private boolean pointInView(float localX, float localY, float slop) {
    return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
            localY < ((mBottom - mTop) + slop);
}

mTouchSlop is set in the constructor

mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

You can extend View and set mTouchSlop to zero. I don't see an other way to set mTouchSlop. There is no function like getApplicationContext.setScaledTouchSlop(int n).

2
  • Thank you for your answer - this does seem like the value in question, so at least I know what the blasted thing is called now. Unfortunately, while I can change the value using reflection, it has no effect. I even tried changing it for all of the view's parents - no luck. Making it bigger does nothing, either. My guess is that maybe the TouchSlop value is applied before the event gets to my view. It seems to be hardcoded in a config.xml file in the source code for the system, and I can't figure out if there's a way to change it without rebuilding the whole thing. Thanks for your help.
    – Erhannis
    Mar 10, 2012 at 4:16
  • If you are looking at recycler views there is such a function. recyclerView.setScrollingTouchSlop(TOUCH_SLOP_PAGING) developer.android.com/reference/android/support/v7/widget/…
    – Grez.Kev
    Apr 19, 2018 at 22:50
1

Extend View Class.

Override pointInView method without "@Override" annotaion and set touchSlop = 0:

public boolean pointInView(float localX, float localY, float slop) {
    slop = 0;
    return localX >= -slop && localY >= -slop && localX < (getWidth() + slop) &&
            localY < (getBottom() + slop);
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.