I am creating softkeyboard for android 2.2 and higher. everything is fine but when i type really quick then some time my ACTION_DOWN method is not calling. Actual flow of called method should look like
1) motionEvent.ACTION_DOWN
2) OnPress()
3) motionEvent.ACTION_UP
4) OnRelease() and repeat same order for next word.
if i type at normal speed then it works fine but if i type fast then above order of method execution looks like
1) motionEvent.ACTION_DOWN
2) OnPress()
3) OnRelease()
4) motionEvent.ACTION_UP and for next word OnPress and OnRelease() methods are being called.
any suggestions?
Edit My LatinKeyboardView class that contains 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;
}
and my SoftKeyboard class..
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);
}
}
Thanks..
This is a very long onTouchEvent
handler, I suggest breaking it up into more logical steps. I also have had the issue of seemingly "out-of-order" events when trying to handle the touchscreen.
I found that I wasn't handling the events per pointer ID correctly. I'd check to make sure you are handling multiple pointers as expected. The device I test with (N1) only supports two pointers, but others support many more, and those should be accounted for.
For handling touchscreen "soft-buttons" as an onTouchEvent
event, I've found it useful to create a state machine class. Use the MotionEvent
parameters as input events to the state machine, and cause state transitions to trigger your wanted events. An explicit, state-driven approach will give you the expected results you are looking for.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With