MotionEvent Issues

前端 未结 2 2010
礼貌的吻别
礼貌的吻别 2021-01-07 09:21

I was wondering how to get accurate, get(x) and get(y) values for a MotionEvent? What is happening is that when I touch a specific area on the screen, I tell an action to ha

相关标签:
2条回答
  • 2021-01-07 09:56

    I don't have a tip but a working solution and some tips for multitouch starters. I never did that before so after seeing your question I was eager to now how multitouch works. And actually it's pretty tricky to get it done right. This is a great example for startes (view with four corners which are colored when one of the pointers is touching it - works with one up to five pointers / fingers). So I will explain it based on the working solution posted further below.

    Basically it works like this: getPointerCount() is giving you the amount of currently touching pointers. getX(i) and getY(i) are giving you the corresponding coordinates. But this part is tricky: pointer 0, 1, 2 are touching, you lift 1 (second finger), and now you have 0, 1. This is the point where getPointerId(1) will return 2 because this is still your pointer 2 which is touching but it's now on position 1 instead 2. You have a single chance to react on that change, that's when ACTION_POINTER_UP is fired. On this action and on ACTION_POINTER_DOWN getActionIndex() will return the action pointer position of the pointer which was added or removed from pointer event list. Otherwise it's returning always 0 and that's something nobody tells you.

    The conclusion is that you can't actually track which pointer is moving (that's why getActionIndex() is return 0 on move). That seems ridiculously logical because it would be stupid to fire 5 separate events for each finger. But while starting with multitouch that's a really non trivial fact ;-)

    Here's the example. coordsX and coordsY will track the pointer coordinates and if position 2 contains valid coordinates, it means that this is your third finger which is still touching the screen whether you lifted the other fingers or not. This might not be relevant in this case but it's a fact which could still be useful in other multitouch implementations.

    public class MultitouchView extends LinearLayout {
    
        // we have a maximum of 5 pointer
        // we will set Integer.MIN_VALUE if the pointer is not present
        private int [] coordsX = new int [5];
        private int [] coordsY = new int [5];
    
        // add 4 views with a certain gravity so they are placed in corners
        public MultitouchView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            // initialize as not present
            Arrays.fill(coordsX, Integer.MIN_VALUE);
            Arrays.fill(coordsY, Integer.MIN_VALUE);
    
    
            /* the layout is inflated from a XML file and is basically this one:
             * all subviews are matching / filling parent and have a weight of 1
             * <LinearLayout vertical>
             *   <LinearLayout horizontal>
             *     <View /><View />
             *   </LinearLayout>
             *   <LinearLayout horizontal>
             *     <View /><View />
             *   </LinearLayout>
             * </LinearLayout>
             */
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent e) {
            if (e.getAction() == MotionEvent.ACTION_CANCEL) {
                // every touch is going to be canceled, clear everything
                Arrays.fill(coordsX, Integer.MIN_VALUE);
                Arrays.fill(coordsY, Integer.MIN_VALUE);
            } else {
                // track all touches
                for (int i = 0; i < e.getPointerCount(); i++) {
                    int id = e.getPointerId(i);
    
                    if (e.getActionIndex() == i
                            && (e.getActionMasked() == MotionEvent.ACTION_POINTER_UP
                            || e.getActionMasked() == MotionEvent.ACTION_UP)) {
                        // pointer with this id is about to leave, clear it
                        coordsX[id] = coordsY[id] = Integer.MIN_VALUE;
                    } else {
                        // update all other pointer positions
                        coordsX[id] = (int) e.getX(i);
                        coordsY[id] = (int) e.getY(i);
                    }
                }
            }
    
            int hw = getWidth() / 2;
            int hh = getHeight() / 2;
    
            // first no corner is touched
            boolean topLeft = false, topRight = false, bottomRight = false, bottomLeft = false;
    
            // check if any of the given pointer is touching a certain corner
            for (int i = 0; i < coordsX.length; i++) {
                // pointer is not active (anymore)
                if (coordsX[i] == Integer.MIN_VALUE || coordsY[i] == Integer.MIN_VALUE) {
                    continue;
                }
    
                topLeft = topLeft || coordsX[i] < hw && coordsY[i] < hh;
                topRight = topRight || coordsX[i] > hw && coordsY[i] < hh;
                bottomRight = bottomRight || coordsX[i] > hw && coordsY[i] > hh;
                bottomLeft = bottomLeft || coordsX[i] < hw && coordsY[i] > hh;
            }
    
            // set the result we have now to the views represnting the corners
            ((ViewGroup) getChildAt(0)).getChildAt(0).setBackgroundColor(topLeft ? 0xffffff00 : 0x0);
            ((ViewGroup) getChildAt(0)).getChildAt(1).setBackgroundColor(topRight ? 0xffff0000 : 0x0);
            ((ViewGroup) getChildAt(1)).getChildAt(0).setBackgroundColor(bottomLeft ? 0xff00ff00 : 0x0);
            ((ViewGroup) getChildAt(1)).getChildAt(1).setBackgroundColor(bottomRight ? 0xff0000ff : 0x0);
    
            return true;
        }
    }
    
    0 讨论(0)
  • 2021-01-07 09:56

    You should use ‘motionEvent.getPoinerCount()‘ to check the number of touch points.

    0 讨论(0)
提交回复
热议问题