Estoy creando un teclado programable para Android 2.2 y superior. todo está bien, pero cuando escribo muy rápido, en algún momento mi método ACTION_DOWN no ​​llama. El flujo real del método llamado debería verse como

                    1) motionEvent.ACTION_DOWN
                    2) OnPress()   
                    3) motionEvent.ACTION_UP
                    4) OnRelease() and repeat same order for next word.

Si escribo a velocidad normal, entonces funciona bien, pero si escribo rápido, el orden de ejecución del método anterior se ve así

                 1) motionEvent.ACTION_DOWN
                 2) OnPress() 
                 3) OnRelease()
                 4) motionEvent.ACTION_UP  and for next word OnPress and OnRelease() methods are being called.

¿alguna sugerencia?

Editar Mi clase LatinKeyboardView que contiene MotionActionEvents

enter code here @Override
public boolean onTouchEvent(MotionEvent me) {
    // Moved next line and added lines to help solve reentrant problem.
    int action = me.getAction();
    // next 2 lines required for multitouch Andr 2+
    int act = action & MotionEvent.ACTION_MASK;
    final int ptrIndex = (act & MotionEvent.ACTION_POINTER_ID_MASK) //Renamed to ACTION_POINTER_INDEX_MASK in later Andro versions
    >> MotionEvent.ACTION_POINTER_ID_SHIFT;//Renamed to ACTION_POINTER_INDEX_SHIFT in later Andro versions

//      currentX = me.getX();
//      currentY = me.getY();
        calcMinSlide();

//      int act = me.getAction();
        if (act == android.view.MotionEvent.ACTION_DOWN) {
            Log.v(tag, "ANGLE_ACTION_DOWN : ");


        if (pw != null) {
            pw.dismiss();
            pw = null;
        }
        lastDirection = direction = 0;
        touchDownPoint.set(me.getX(), me.getY());

        // Will added next two lines
        touchDragPoint.set(me.getX(), me.getY());
        thresholdPoint.set(me.getX(), me.getY());
        // Will6 added to improve accuracy
        thresholdPoint1_5 = false;
        // Will7 added next 4 for Andro 2+
        currentX = me.getX();
        currentY = me.getY();
        // Save the ID of this first pointer (touch) down
        currentPointerID = me.getPointerId(0);
        nextPointerID = INVALID_POINTER_ID;

        previousDownTime = me.getEventTime();
        me.setLocation(touchDownPoint.x, touchDownPoint.y);
        // start timer on touch down
        startTimer(me, 300); // 150); Will7 changed this and removed method: checkLongPress

    } else if (act == android.view.MotionEvent.ACTION_UP
                || act == android.view.MotionEvent.ACTION_MOVE) {

        Log.v(tag, "ANGLE_ACTION_UP : ");
        //touchdragPoint and previoustouchPoint for calculating velocity
        PointF previousTouchPoint = new PointF(touchDragPoint.x,touchDragPoint.y);

        //Will7 added next if for Andro 2+: Find the index of the active pointer and fetch its position
        if (act == android.view.MotionEvent.ACTION_MOVE &&  me.getPointerId(ptrIndex) != currentPointerID) { 
            //Log.v(tag, "Cancel ATION_MOVE!! ID: "+me.getPointerId(ptrIndex));
            return super.onTouchEvent(me);
        }
        touchDragPoint.set(me.getX(), me.getY());           
        dy = me.getY() - touchDownPoint.y;
        dx = me.getX() - touchDownPoint.x;

        // added for Andro 2+
        currentX = touchDragPoint.x;
        currentY = touchDragPoint.y;

        //calculate time interval from down time to current time
        long timeInterval = me.getEventTime() - previousDownTime;
        previousDownTime = me.getEventTime();
        velocityThresDir = VELOCITY_THRESHOLD;
        float touchVelocity = Math.abs(distanceBetweenPoints(touchDragPoint, previousTouchPoint) / timeInterval);

        if (distanceFromCenter(dx,dy) > minSlide) {
//              Log.v(tag, "direction to detect angle....after... dx..."+dx+" dy "+dy);
                //Log.v(tag, "ANGLE angle.... after..."+distanceFromCenter(dx,dy)+" slide distance "+ minSlide);


            /* cancel the timer*/
            if (cDownTimer != null) {
                cDownTimer.cancel();
                cDownTimer = null;
            }
            /* coding for calculating velocity threshold*/          
            float angleThreshold = 0.0f;
            if ((thresholdPoint.x == touchDownPoint.x) && (thresholdPoint.y == touchDownPoint.y)){
                thresholdPoint.set(touchDragPoint.x, touchDragPoint.y);
            }
            else {
                //Will6 - added next if to improve accuracy
                if ((distanceFromCenter(dx,dy) > (minSlide * 1.5)) && !thresholdPoint1_5){
                    thresholdPoint.set(me.getX(),me.getY());
                    thresholdPoint1_5 = true;
                }
                float angleP1= calcAngle(touchDownPoint, thresholdPoint);
                float angleP2= calcAngle(previousTouchPoint, touchDragPoint);
                angleThreshold = Math.abs(angleP1 - angleP2);
                if (angleThreshold > Math.PI) angleThreshold = (float) (2.0 * Math.PI) - angleThreshold;
            }
//              velocityThresDir = (float) Math.abs((Math.cos(angleThreshold) * touchVelocity*1000));
                velocityThresDir = (float) (Math.cos(angleThreshold) * touchVelocity*1000);

            //end of calculation for velocity threshold 


            double angle = newM(touchDownPoint.x, touchDownPoint.y, touchDragPoint.x, touchDragPoint.y);
//              Log.v(tag, "ANGLE_FIRST_X "+touchDownPoint.x+"FIRST_Y "+touchDownPoint.y);
//              Log.v(tag, "ANGLE_SECOND_X "+touchDragPoint.x+"SECOND_Y "+touchDragPoint.y);
//              Log.v(tag, "ANGLE_FIRST"+angle);

            if ((touchDownPoint.x != thresholdPoint.x) || (touchDownPoint.y != thresholdPoint.y)) {
                double angleThresh = newM(touchDownPoint.x, touchDownPoint.y, thresholdPoint.x, thresholdPoint.y);
                double angleBetween = Math.abs(angle - angleThresh);
                if(angleBetween < 45 || angleBetween > 315){
                    if(angleBetween > 315) {
                        if (angle < angleThresh) {
                            angle += 360;
                        }
                        else if (angle > angleThresh) {
                            angleThresh += 360;
                        }
                        angle = Math.abs((angle - angleThresh)%360) / 2.0;
//                          Log.v(tag, "ANGLE_SECOND"+angle);
                        }
                        else {
                            angle = (angle + angleThresh * 1.0) / 2.0;
//                          Log.v(tag, "ANGLE_THIRD"+angle);
                        }
                    }
                }

            if (angle > 337.5){ 
                direction = 3;
            }else if (angle > 292.5){ 
                direction = 5;
            }else if (angle > 247.5){ 
                direction = 4;
            }else if (angle > 202.5){ 
                direction = 6;
            }else if (angle > 157.5){ 
                direction = 1;
            }else if (angle > 112.5){ 
                direction = 7;
            }else if (angle >  67.5){ 
                direction = 2;
            }else if (angle >  22.5){ 
                direction = 8;
            }else{
                direction = 3;
            }

            /* start timer if velocity is below velocity threshold*/    
            if ((velocityThresDir < VELOCITY_THRESHOLD) &&
                    (act == android.view.MotionEvent.ACTION_MOVE) && (cDownTimer == null) &&
                    (pw == null)) { //"&& cDownTimer" can be removed I think
                /* start timer with motionEvent and time in ms as a parameter */
                // added next two lines
                callOnLongPress(me);
                startTimerShowPopup(me,100);//Will changed from 150
             }          
        } else {
            direction = 0;
        }

        if (act == android.view.MotionEvent.ACTION_MOVE) {
            return true;
        } else if (act == android.view.MotionEvent.ACTION_UP) {
            if (cDownTimer != null) {
                cDownTimer.cancel();
                cDownTimer = null;
            }
            if (pw != null)
                pw.dismiss();
            if (longPressedKey) {
                SoftKeyboard.mComposing
                        .append(charset[mappedKey][direction]);
                popUpTextEntryScheme = true;
            }

            longPressedKey = false;
            currentPointerID = INVALID_POINTER_ID;
        }
    }

    else if (act == android.view.MotionEvent.ACTION_POINTER_DOWN) {
        //          if (me.getPointerCount() > 1) { //Should always be true, I think
            nextPointerID = me.getPointerId(ptrIndex);
            nextTouchDownPoint.set(me.getX(ptrIndex),me.getY(ptrIndex));
//          }
        } 
        else if (act == android.view.MotionEvent.ACTION_CANCEL) {
            currentPointerID = INVALID_POINTER_ID;
            nextPointerID  = INVALID_POINTER_ID;

    }
    else if (act == android.view.MotionEvent.ACTION_POINTER_UP) {
        // Extract the index of the pointer that left the touch sensor
        final int pointerId = me.getPointerId(ptrIndex);
        if (pointerId == currentPointerID) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            final int newPointerIndex = ptrIndex == 0 ? 1 : 0;
            currentPointerID = nextPointerID;//(0);
            touchDownPoint.set(nextTouchDownPoint.x,nextTouchDownPoint.y);
            if (cDownTimer != null) {
                cDownTimer.cancel();
                cDownTimer = null;
            }
            if (pw != null) {
                pw.dismiss();
                pw = null;
            }
            if (longPressedKey) {
                SoftKeyboard.mComposing
                        .append(charset[mappedKey][direction]);
                popUpTextEntryScheme = true;
            }
            longPressedKey = false;
            lastDirection = direction = 0; // keysAtOnce=0;

            touchDragPoint.set(me.getX(newPointerIndex),me.getY(newPointerIndex));
            thresholdPoint.set(nextTouchDownPoint.x,nextTouchDownPoint.y);
            //added to improve accuracy
            thresholdPoint1_5 = false;
            // added next 3 for Andro 2+
            currentX = touchDragPoint.x;
            currentY = touchDragPoint.y;
            // Save the ID of this first pointer (touch) down

            previousDownTime = me.getEventTime();
            me.setLocation(touchDownPoint.x, touchDownPoint.y);
            //start timer on touch down     
            startTimer(me,300); //150); Will7 changed this and removed method: checkLongPress   
        } else { //Second pointer up before first. (Not handling 3 or more pointers yet!)

//              nextPointerID  = INVALID_POINTER_ID;
            }
        }   //else



    return super.onTouchEvent(me); // after we return here the service will get notified, etc
//      return true;
    }

Y mi clase SoftKeyboard ...

    public void onPress(int primaryCode) {
        Log.v("SoftKeyboard", "ANGLE_ACTION_ON_PRESS : ");

        //  added next section for repeating backspace
        if (RepeatBSTimer != null) {
            RepeatBSTimer.cancel();
            RepeatBSTimer = null;
        }
        if (mp != null) { // /Will7 moved this from just above keystroke
                            // statement
            mp.release();
            mp = null;
        }

        //  added for Andro 2+ multitouch
        if (primaryCode == pressedCode
                && LatinKeyboardView.nextPointerID != LatinKeyboardView.INVALID_POINTER_ID) {
            // I need to look up the real primaryCode here. (Not sure how!)
            // Android gives wrong values when touches overlap.
            wrongPrimaryCode = true;
            return;
        } else
            wrongPrimaryCode = false;

        pressedCode = primaryCode;

        //  added next section for repeating backspace
        if (primaryCode == Keyboard.KEYCODE_DELETE) {
            RepeatBSTimer = new CountDownTimer(1500000, 75) {
                @Override
                public void onTick(long millisUntilFinished) {
                    int primaryCode2;
                    if (LatinKeyboardView.longPressedKey
                            || (1500000 - millisUntilFinished > 500)) {
                        primaryCode2 = getCharFromKey(pressedCode,
                                LatinKeyboardView.direction, mInputView
                                        .getKeyboard());
                        if (primaryCode2 == Keyboard.KEYCODE_DELETE) {
                            repeating = true;
                            handleBackspace();
                        } else if (primaryCode2 == KEYCODE_DELETEWORD
                                && (millisUntilFinished % 150) < 75) {
                            repeating = true;
                            deleteLastWord();
                        }
                    }
                }

                @Override
                public void onFinish() {
                }
            };
            RepeatBSTimer.start();
        }
        //  added section for repeating backspace

        Uri uri = Uri.parse("android.resource://" + getPackageName() + "/"
                + R.raw.keystroke);// Play Key Click
        try {
            mp = new MediaPlayer();
            mp.setDataSource(this, uri);
            mp.prepare();
            mp.start();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
}

public void onRelease(int primaryCode) {

        // Will7 added next line if for Andro 2+ multitouch
        if (wrongPrimaryCode
                && LatinKeyboardView.nextPointerID != LatinKeyboardView.INVALID_POINTER_ID) {

            return;
        }
        // else pressedCode = primaryCode;


        //  added next sections for repeating backspace
        primaryCode = getCharFromKey(pressedCode, LatinKeyboardView.direction,mInputView.getKeyboard());
        if (primaryCode == Keyboard.KEYCODE_DELETE && !repeating)
            handleBackspace();
        if (primaryCode == KEYCODE_DELETEWORD && !repeating)
            deleteLastWord();
        repeating = false;

        if (RepeatBSTimer != null) {
            RepeatBSTimer.cancel();
            RepeatBSTimer = null;
        }
        //  moved all the rest of this method from onKey()
        int[] keyCodes;

        //  added this var for Andro 2+ multitouch
        keyCodes = keyCodesSave;

        commitTyped(getCurrentInputConnection());


        if (isWordSeparator(primaryCode) && (char) primaryCode != '.'

                && (char) primaryCode != '!' && (char) primaryCode != '?') {
            // Handle separator
            if (mComposing.length() > 0) {
                commitTyped(getCurrentInputConnection());
            }
            sendKey(primaryCode);
            updateShiftKeyState(getCurrentInputEditorInfo());
        } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
            //  commented out next line for repeating backspace
            // handleBackspace();
        } else if (primaryCode == Keyboard.KEYCODE_SHIFT || primaryCode == -1) {
            handleShift();
        } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {
            handleClose();
            return;
        } else if (primaryCode == KEYCODE_ESCAPE) {
            // Do nothing on Escape key
        } else if (primaryCode == LatinKeyboardView.KEYCODE_OPTIONS) {
            // Show a menu or something
        } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE
                && mInputView != null) {
            Keyboard current = mInputView.getKeyboard();
            if (current == mSymbolsKeyboard
                    || current == mSymbolsShiftedKeyboard) {
                getCurrentInputConnection().finishComposingText();
                current = mQwertyKeyboard;
            } else {
                getCurrentInputConnection().finishComposingText();
                current = mSymbolsKeyboard;
            }
            mInputView.setKeyboard(current);
            if (current == mSymbolsKeyboard) {
                current.setShifted(false);
            }
        } else if (primaryCode == KEYCODE_CAPSLOCK)// handle caps lock
        {
            if (mInputView.getKeyboard() == mQwertyKeyboard
                    || mInputView.getKeyboard() == mSymbolsKeyboard) {
                mInputView.setKeyboard(mQwertyKeyboardUpperCase);
                mQwertyKeyboardUpperCase.setShifted(true);
                mCapsLock = true;
            } else {
                mQwertyKeyboard.setShifted(false);
                mInputView.setKeyboard(mQwertyKeyboard);
                mCapsLock = false;
            }
        } else if (primaryCode == KEYCODE_DELETEWORD) {
            //  commented out next line for repeating backspace
            // deleteLastWord();
        } else if (primaryCode == KEYCODE_FULL_STOP_AND_SPACE) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) '.', keyCodes);
            handleCharacter((int) ' ', keyCodes);
            handleShift();

        }
        //  added next 5 KEYCODES
        else if (primaryCode == KEYCODE_EXCLAMATION) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) '!', keyCodes);
            handleCharacter((int) ' ', keyCodes);
            handleShift();

        } else if (primaryCode == KEYCODE_QUESTION_MARK) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) '?', keyCodes);
            handleCharacter((int) ' ', keyCodes);
            handleShift();

        } else if (primaryCode == KEYCODE_COMMA) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) ',', keyCodes);
            handleCharacter((int) ' ', keyCodes);

        } else if (primaryCode == KEYCODE_COLON) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) ':', keyCodes);
            handleCharacter((int) ' ', keyCodes);

        } else if (primaryCode == KEYCODE_SEMICOLON) {
            //  added next line
            backspaceIfSpaceLeft();
            getCurrentInputConnection().finishComposingText();

            handleCharacter((int) ';', keyCodes);
            handleCharacter((int) ' ', keyCodes);

        } else {
            handleCharacter(primaryCode, keyCodes);
        }
}

Gracias..

7
Piyush 12 mar. 2012 a las 15:26
2
Sin ver su código, nadie puede sugerirle nada, así que publique la parte principal de su código ...
 – 
himanshu
22 mar. 2012 a las 09:50

1 respuesta

La mejor respuesta

Este es un controlador onTouchEvent muy largo, sugiero dividirlo en pasos más lógicos. También he tenido el problema de eventos aparentemente "desordenados" al tratar de manejar la pantalla táctil.

Descubrí que no estaba manejando los eventos por ID de puntero correctamente. Verificaría para asegurarme de que está manejando múltiples punteros como se esperaba. El dispositivo con el que pruebo (N1) solo admite dos punteros, pero otros admiten muchos más, y esos deben tenerse en cuenta.

Para manejar los "botones suaves" de la pantalla táctil como un evento onTouchEvent, me ha resultado útil crear una clase de máquina de estado. Utilice los parámetros MotionEvent como eventos de entrada a la máquina de estado y haga que las transiciones de estado activen sus eventos deseados. Un enfoque explícito impulsado por el estado le dará los resultados esperados que está buscando.

2
Kaiged 29 mar. 2012 a las 08:34