Android – non riesce a catturare il backspace / cancellare premere in soft. tastiera

Sto sovrascrivendo il metodo onKeyDown della vista (vista di superficie di openGL) per catturare tutte le prese di tasti. Il problema è che su più dispositivi il KEYCODE_DEL non viene catturato. Ho provato ad aggiungere un onKeyListener alla vista e che ha catturato tutto tranne il tasto backspace.

Deve esserci un modo per ascoltare questo evento di printing, ma come?

  • Ha provato molte risposte per get il mio keytool.exe per aprire ma fallito
  • Può un'applicazione Android collegarsi direttamente a un database mysql online
  • Linee di codice che cambiano e vanno; problemi con l'editor XML di Eclipse
  • Videochat in android
  • come riempire il colore dell'image in base alla percentuale di visualizzazione di text in android
  • MPAndroidChart - Le etichette di Legend vengono tagliate
  • Lancia un'applicazione dalla mia applicazione e ottieni la risposta di aprire la seconda app
  • Android - .nomedia non funziona per le immagini
  • Come posso testare il risultato di un button che cambia in modo asincrono la vista dell'attività?
  • Sviluppo Android: Utilizzo di immagini dai beni in un HTML di WebView
  • Perché demandLayout viene richiamato direttamente dopo l'invalidazione
  • Spedire un'applicazione con un database
  • 11 Solutions collect form web for “Android – non riesce a catturare il backspace / cancellare premere in soft. tastiera”

    11/12/2014 AGGIORNAMENTO: Modificato l'ambito di correzione per non limitare al livello <API 19, in quanto a una tastiera di terze parti c'è ancora il bug oltre il 19.

    1/9/2014 UPDATE: ho elaborato un approccio con codice per risolvere tutti i problemi KEYCODE_DEL di Google Keyboard (LatinIME), in particolare rilascia 42904 e 62306.

    Il miglioramento della risposta di Turix è stato incorporato, con permesso, nel mio codice qui . I miglioramenti di Turix rimossi devono iniettare i caratteri di spazzatura nel tampone Editable, trovando invece un modo incrementale per assicurarsi che esattamente un carattere fosse sempre in quel buffer.

    Ho usato un codice simile (simile) in un'applicazione distribuita che puoi provare:
    https://play.google.com/store/apps/details?id=com.goalstate.WordGames.FullBoard.trialsuite%5D

    INTRODUZIONE:

    La soluzione proposta qui sotto è destinata a funzionare per tutte le versioni della tastiera di Google, passata e futura, per quanto riguarda questi due bug. Questa soluzione non richiede che un'applicazione rimanga bloccata per il targeting del livello di API 15 o inferiore, che alcune applicazioni si sono limitate a utilizzare il codice di compatibilità che viene rilasciato nell'edizione 42904.

    Questi problemi sono presenti solo come bug per una vista che ha implementato l'override per onCreateInputConnection () e che restituisce TYPE_NULL all'invocazione IME (nel membro inputType dell'argomento EditorInfo passato a tale metodo dall'IME). Solo facendo questo, una vista può ragionevolmente prevedere che gli events chiave (inclusi KEYCODE_DEL) verranno restituiti da una tastiera soft. Di conseguenza, la soluzione proposta qui richiede il TYPE_NULL InputType.

    Per le applicazioni che non utilizzano TYPE_NULL, esistono diversi override nell'object derivato da BaseInputConnection restituito da una vista dal suo override onCreateInputConnection () che vengono richiamati dall'IME quando l'utente effettua le modifiche, anziché gli events chiave di generazione IME. Questo approccio (non TYPE_NULL) è di solito superiore, perché le funzionalità della tastiera soft si estendono ben oltre il semplice touch delle chiavi, a cose come l'input vocale, il completamento, ecc. Gli events chiave sono un metodo più vecchio e quelli che implementano LatinIME a Google hanno detto che vorrebbero vedere l'uso di TYPE_NULL (e gli events chiave) andare via.

    Se si interrompe l'uso di TYPE_NULL è un'opzione, ti invito a procedere con l'approccio consigliato di utilizzare i methods di override di InputConnection anziché gli events chiave (o, più semplicemente, utilizzando una class derivata da EditText, che lo fa per te ).

    Tuttavia, il comportmento TYPE_NULL non viene interrotto ufficialmente, e quindi il fallimento di events di KEYCODE_DEL in determinate circostanze è in realtà un bug. Offro la seguente soluzione per affrontare questo problema.

    PANORAMICA:

    I problemi che le applicazioni hanno avuto nel ricevere KEYCODE_DEL da Latinime sono dovute a due errori noti, come riportto qui :

    https://code.google.com/p/android/issues/detail?id=42904 (elencato come WorkingAsIntended, ma il problema è che mantiene un bug in quanto provoca un errore di supporto per la generazione di events KEYCODE_DEL per targeting delle app API a livello 16 e superiore che hanno specificato in modo specifico un tipo di input di tipo TYPE_NULL. Il problema è corretto nelle ultime versioni di Latinime, ma esistono pubblicazioni passate in natura che ancora presentano questo errore e quindi le applicazioni che utilizzano TYPE_NULL e targeting API Level 16 o sopra avrà ancora bisogno di una soluzione alternativa che può essere eseguita dall'interno dell'app.

    e qui :

    http://code.google.com/p/android/issues/detail?id=62306 (attualmente elencato come fisso ma non ancora rilasciato – FutureRelease – ma anche una volta che viene rilasciato, avremo ancora bisogno di una soluzione alternativa che può essere eseguita da dentro l'app per affrontare le versioni precedenti che persisteranno "in natura").

    In linea con questa tesi (che i problemi riscontrati con gli events KEYCODE_DEL sono dovuti a bug in Latinime), ho scoperto che quando si utilizza una tastiera hardware esterna e anche quando si utilizza la tastiera soft swiftkey di terza parte, questi problemi non si verificano si verificano per versioni specifiche di Latinime.

    L'uno o l'altro (ma non entrambi contemporaneamente) di questi problemi è presente in alcune release di Latinime. Di conseguenza, è difficile per gli sviluppatori conoscere durante il test se hanno lavorato in tutti i problemi KEYCODE_DEL e, a volte, quando viene eseguito un aggiornamento Android (o Google Keyboard), un problema non sarà più riproducibile durante il test. Tuttavia, le versioni LatinIME che causano il problema saranno presenti su un certo numero di dispositivi in ​​uso. Questo mi ha costretto a scavare nel repo di git repository di AOSP Latinime per determinare l'esatto ambito di ciascuno dei due problemi (vale a dire le specifiche Latinime e Android, le versioni per le quali possono essere presenti entrambi i due problemi). Il codice di soluzione alternativa di seguito è stato limitato a quelle versioni specifiche.

    Il codice di soluzione alternativa presentato di seguito include commenti estensivi che dovrebbero aiutare a capire che cosa sta tentando di realizzare. A seguito della presentazione del codice, fornirò ulteriori discussioni, che includerà l'apposito Android Open Source Project (AOSP) che impegna in cui ciascuno dei due bug è stato introdotto e in cui è scomparso, nonché le versioni Android che potrebbero includere le pubblicazioni interessate della tastiera di Google.

    Vorrei avvisare chiunque pensasse di utilizzare questo approccio per eseguire i propri test per verificare che funzioni per la loro app particolare. Penso che functionrà in generale e lo abbia provato su un certo numero di dispositivi e nelle versioni Latinime, ma il ragionamento è complicato, quindi procedi con caucanvas. Se troverai dei problemi, ti preghiamo di submit un commento qui sotto.

    CODICE :

    Ecco allora, è la mia soluzione per entrambi i problemi, con una spiegazione inclusa nei commenti al codice:

    Innanzitutto, includa la seguente class (modificata a piacere) nell'applicazione, nel proprio file di origine InputConnectionAccomodatingLatinIMETypeNullIssues.java:

    import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.BaseInputConnection; /** * * @author Carl Gunther * There are bugs with the LatinIME keyboard's generation of KEYCODE_DEL events * that this class addresses in various ways. These bugs appear when the app * specifies TYPE_NULL, which is the only circumstance under which the app * can reasonably expect to receive key events for KEYCODE_DEL. * * This class is intended for use by a view that overrides * onCreateInputConnection() and specifies to the invoking IME that it wishes * to use the TYPE_NULL InputType. This should cause key events to be returned * to the view. * */ public class InputConnectionAccomodatingLatinIMETypeNullIssues extends BaseInputConnection { //This holds the Editable text buffer that the LatinIME mistakenly *thinks* // that it is editing, even though the views that employ this class are // completely driven by key events. Editable myEditable = null; //Basic constructor public InputConnectionAccomodatingLatinIMETypeNullIssues(View targetView, boolean fullEditor) { super(targetView, fullEditor); } //This method is called by the IME whenever the view that returned an // instance of this class to the IME from its onCreateInputConnection() // gains focus. @Override public Editable getEditable() { //Some versions of the Google Keyboard (LatinIME) were delivered with a // bug that causes KEYCODE_DEL to no longer be generated once the number // of KEYCODE_DEL taps equals the number of other characters that have // been typed. This bug was reported here as issue 62306. // // As of this writing (1/7/2014), it is fixed in the AOSP code, but that // fix has not yet been released. Even when it is released, there will // be many devices having versions of the Google Keyboard that include the bug // in the wild for the indefinite future. Therefore, a workaround is required. // //This is a workaround for that bug which just jams a single garbage character // into the internal buffer that the keyboard THINKS it is editing even // though we have specified TYPE_NULL which *should* cause LatinIME to // generate key events regardless of what is in that buffer. We have other // code that attempts to ensure as the user edites that there is always // one character remaining. // // The problem arises because when this unseen buffer becomes empty, the IME // thinks that there is nothing left to delete, and therefore stops // generating KEYCODE_DEL events, even though the app may still be very // interested in receiving them. // //So, for example, if the user taps in ABCDE and then positions the // (app-based) cursor to the left of A and taps the backspace key three // times without any evident effect on the letters (because the app's own // UI code knows that there are no letters to the left of the // app-implemented cursor), and then moves the cursor to the right of the // E and hits backspace five times, then, after E and D have been deleted, // no more KEYCODE_DEL events will be generated by the IME because the // unseen buffer will have become empty from five letter key taps followed // by five backspace key taps (as the IME is unaware of the app-based cursor // movements performsd by the user). // // In other words, if your app is processing KEYDOWN events itself, and // maintaining its own cursor and so on, and not telling the IME anything // about the user's cursor position, this buggy processing of the hidden // buffer will stop KEYCODE_DEL events when your app actually needs them - // in whatever Android releases incorporate this LatinIME bug. // // By creating this garbage characters in the Editable that is initially // returned to the IME here, we make the IME think that it still has // something to delete, which causes it to keep generating KEYCODE_DEL // events in response to backspace key presses. // // A specific keyboard version that I tested this on which HAS this // problem but does NOT have the "KEYCODE_DEL completely gone" (issue 42904) // problem that is addressed by the deleteSurroundingText() override below // (the two problems are not both present in a single version) is // 2.0.19123.914326a, tested running on a Nexus7 2012 tablet. // There may be other versions that have issue 62306. // // A specific keyboard version that I tested this on which does NOT have // this problem but DOES have the "KEYCODE_DEL completely gone" (issue // 42904) problem that is addressed by the deleteSurroundingText() // override below is 1.0.1800.776638, tested running on the Nexus10 // tablet. There may be other versions that also have issue 42904. // // The bug that this addresses was first introduced as of AOSP commit tag // 4.4_r0.9, and the next RELEASED Android version after that was // android-4.4_r1, which is the first release of Android 4.4. So, 4.4 will // be the first Android version that would have included, in the original // RELEASED version, a Google Keyboard for which this bug was present. // // Note that this bug was introduced exactly at the point that the OTHER bug // (the one that is addressed in deleteSurroundingText(), below) was first // FIXED. // // Despite the fact that the above are the RELEASES associated with the bug, // the fact is that any 4.x Android release could have been upgraded by the // user to a later version of Google Keyboard than was present when the // release was originally installed to the device. I have checked the // www.archive.org snapshots of the Google Keyboard listing page on the Google // Play store, and all released updates listed there (which go back to early // June of 2013) required Android 4.0 and up, so we can be pretty sure that // this bug is not present in any version earlier than 4.0 (ICS), which means // that we can limit this fix to API level 14 and up. And once the LatinIME // problem is fixed, we can limit the scope of this workaround to end as of // the last release that included the problem, since we can assume that // users will not upgrade Google Keyboard to an EARLIER version than was // originally included in their Android release. // // The bug that this addresses was FIXED but NOT RELEASED as of this AOSP // commit: //https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+ // /b41bea65502ce7339665859d3c2c81b4a29194e4/java/src/com/android // /inputmethod/latin/LatinIME.java // so it can be assumed to affect all of KitKat released thus far // (up to 4.4.2), and could even affect beyond KitKat, although I fully // expect it to be incorporated into the next release *after* API level 19. // // When it IS released, this method should be changed to limit it to no // higher than API level 19 (assuming that the fix is released before API // level 20), just in order to limit the scope of this fix, since poking // 1024 characters into the Editable object returned here is of course a // kluge. But right now the safest thing is just to not have an upper limit // on the application of this kluge, since the fix for the problem it // addresses has not yet been released (as of 1/7/2014). if(Build.VERSION.SDK_INT >= 14) { if(myEditable == null) { myEditable = new EditableAccomodatingLatinIMETypeNullIssues( EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER); Selection.setSelection(myEditable, 1); } else { int myEditableLength = myEditable.length(); if(myEditableLength == 0) { //I actually HAVE seen this be zero on the Nexus 10 with the keyboard // that came with Android 4.4.2 // On the Nexus 10 4.4.2 if I tapped away from the view and then back to it, the // myEditable would come back as null and I would create a new one. This is also // what happens on other devices (eg, the Nexus 6 with 4.4.2, // which has a slightly later version of the Google Keyboard). But for the // Nexus 10 4.4.2, the keyboard had a strange behavior // when I tapped on the rack, and then tapped Done on the keyboard to close it, // and then tapped on the rack AGAIN. In THAT situation, // the myEditable would NOT be set to NULL but its LENGTH would be ZERO. So, I // just append to it in that situation. myEditable.append( EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER); Selection.setSelection(myEditable, 1); } } return myEditable; } else { //Default behavior for keyboards that do not require any fix return super.getEditable(); } } //This method is called INSTEAD of generating a KEYCODE_DEL event, by // versions of Latin IME that have the bug described in Issue 42904. @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { //If targetSdkVersion is set to anything AT or ABOVE API level 16 // then for the GOOGLE KEYBOARD versions DELIVERED // with Android 4.1.x, 4.2.x or 4.3.x, NO KEYCODE_DEL EVENTS WILL BE // GENERATED BY THE GOOGLE KEYBOARD (LatinIME) EVEN when TYPE_NULL // is being returned as the InputType by your view from its // onCreateInputMethod() override, due to a BUG in THOSE VERSIONS. // // When TYPE_NULL is specified (as this entire class assumes is being done // by the views that use it, what WILL be generated INSTEAD of a KEYCODE_DEL // is a deleteSurroundingText(1,0) call. So, by overriding this // deleteSurroundingText() method, we can fire the KEYDOWN/KEYUP events // ourselves for KEYCODE_DEL. This provides a workaround for the bug. // // The specific AOSP RELEASES involved are 4.1.1_r1 (the very first 4.1 // release) through 4.4_r0.8 (the release just prior to Android 4.4). // This means that all of KitKat should not have the bug and will not // need this workaround. // // Although 4.0.x (ICS) did not have this bug, it was possible to install // later versions of the keyboard as an app on anything running 4.0 and up, // so those versions are also potentially affected. // // The first version of separately-installable Google Keyboard shown on the // Google Play store site by www.archive.org is Version 1.0.1869.683049, // on June 6, 2013, and that version (and probably other, later ones) // already had this bug. // //Since this required at least 4.0 to install, I believe that the bug will // not be present on devices running versions of Android earlier than 4.0. // //AND, it should not be present on versions of Android at 4.4 and higher, // since users will not "upgrade" to a version of Google Keyboard that // is LOWER than the one they got installed with their version of Android // in the first place, and the bug will have been fixed as of the 4.4 release. // // The above scope of the bug is reflected in the test below, which limits // the application of the workaround to Android versions between 4.0.x and 4.3.x. // //UPDATE: A popular third party keyboard was found that exhibits this same issue. It // was not fixed at the same time as the Google Play keyboard, and so the bug in that case // is still in place beyond API LEVEL 19. So, even though the Google Keyboard fixed this // as of level 19, we cannot take out the fix based on that version number. And so I've // removed the test for an upper limit on the version; the fix will remain in place ad // infinitum - but only when TYPE_NULL is used, so it *should* be harmless even when // the keyboard does not have the problem... if((Build.VERSION.SDK_INT >= 14) // && (Build.VERSION.SDK_INT < 19) && (beforeLength == 1 && afterLength == 0)) { //Send Backspace key down and up events to replace the ones omitted // by the LatinIME keyboard. return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); } else { //Really, I can't see how this would be invoked, given that we're using // TYPE_NULL, for non-buggy versions, but in order to limit the impact // of this change as much as possible (ie, to versions at and above 4.0) // I am using the original behavior here for non-affected versions. return super.deleteSurroundingText(beforeLength, afterLength); } } } 

    Quindi, prendere each class derivata dalla vista che deve ricevere events chiave dalla tastiera soft LatinIME e modificarla come segue:

    Innanzitutto, creare un override su onCreateInputConnection () nella vista che deve ricevere gli events chiave come segue:

      @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { //Passing FALSE as the SECOND ARGUMENT (fullEditor) to the constructor // will result in the key events continuing to be passed in to this // view. Use our special BaseInputConnection-derived view InputConnectionAccomodatingLatinIMETypeNullIssues baseInputConnection = new InputConnectionAccomodatingLatinIMETypeNullIssues(this, false); //In some cases an IME may be able to display an arbitrary label for a // command the user can perform, which you can specify here. A null value // here asks for the default for this key, which is usually something // like Done. outAttrs.actionLabel = null; //Special content type for when no explicit type has been specified. // This should be interpnetworkingd (by the IME that invoked // onCreateInputConnection())to mean that the target InputConnection // is not rich, it can not process and show things like candidate text // nor retrieve the current text, so the input method will need to run // in a limited "generate key events" mode. This disables the more // sophisticated kinds of editing that use a text buffer. outAttrs.inputType = InputType.TYPE_NULL; //This creates a Done key on the IME keyboard if you need one outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE; return baseInputConnection; } 

    In secondo luogo, apportre le seguenti modifiche al gestore onKey () per la visualizzazione:

      this.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() != KeyEvent.ACTION_DOWN) { //We only look at ACTION_DOWN in this code, assuming that ACTION_UP is redundant. // If not, adjust accordingly. return false; } else if(event.getUnicodeChar() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.charAt(0)) { //We are ignoring this character, and we want everyone else to ignore it, too, so // we return true indicating that we have handled it (by ignoring it). return true; } //Now, just do your event handling as usual... if(keyCode == KeyEvent.KEYCODE_ENTER) { //Trap the Done key and close the keyboard if it is pressed (if that's what you want to do) InputMethodManager imm = (InputMethodManager) mainActivity.getSystemService(Context.INPUT_METHOD_SERVICE)); imm.hideSoftInputFromWindow(LetterRack.this.getWindowToken(), 0); return true; } else if(keyCode == KeyEvent.KEYCODE_DEL) { //Backspace key processing goes here... return true; } else if((keyCode >= KeyEvent.KEYCODE_A) && (keyCode <= KeyEvent.KEYCODE_Z)) { //(Or, use event.getUnicodeChar() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; 

    Infine, dobbiamo definire una class per il nostro editable che assicura che ci sia sempre alless un carattere nel nostro buffer modificabile:

     import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(CharSequence source) { super(source); } //This character must be ignored by your onKey() code. public static CharSequence ONE_UNPROCESSED_CHARACTER = "/"; @Override public SpannableStringBuilder replace(final int spannableStringStart, final int spannableStringEnd, CharSequence replacementSequence, int replacementStart, int replacementEnd) { if (replacementEnd > replacementStart) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace part of the editable with. //We don't really care about whatever might already be in the editable; // we only care about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, start by zeroing out whatever is there to begin with. super.replace(0, length(), "", 0, 0); //We DO care about preserving the new stuff that is replacing the stuff in the // editable, because this stuff might be sent to us as a keydown event. So, we // insert the new stuff (typically, a single character) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStart, replacementEnd); } else if (spannableStringEnd > spannableStringStart) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is characteristic of a DELETION. // So, start by zeroing out whatever is being replaced in the editable. super.replace(0, length(), "", 0, 0); //And now, we will place our ONE_UNPROCESSED_CHARACTER into the editable buffer, and return it. return super.replace(0, 0, ONE_UNPROCESSED_CHARACTER, 0, 1); } // In this case, NOTHING is being replaced in the editable. This code assumes that there // is already something there. This assumption is probably OK because in our // InputConnectionAccomodatingLatinIMETypeNullIssues.getEditable() method // we PLACE a ONE_UNPROCESSED_CHARACTER into the newly-created buffer. So if there // is nothing replacing the identified part // of the editable, and no part of the editable that is being replaced, then we just // leave whatever is in the editable ALONE, // and we can be confident that there will be SOMETHING there. This call to super.replace() // in that case will be a no-op, except // for the value it returns. return super.replace(spannableStringStart, spannableStringEnd, replacementSequence, replacementStart, replacementEnd); } } 

    Che completa le modifiche di origine che ho trovato sembrano gestire entrambi i problemi.

    NOTE COMPLEMENTARI :

    Il problema descritto dal numero 42904 è stato introdotto nella versione LatinIME consegnata con il livello API 16. Prima di questo, events KEYCODE_DEL sono stati generati indipendentemente dal fatto che sia stato utilizzato TYPE_NULL. Nella versione LatinIME rilasciata con Jelly Bean, questa generazione è stata interrotta, ma non è stata fatta alcuna exception per TYPE_NULL e quindi il comportmento TYPE_NULL è stato effettivamente distriggersto per le applicazioni mirate al di sopra del livello API 16. Tuttavia, è stato aggiunto un codice di compatibilità che consentiva le applicazioni che avevano un targetSdkVersion <16 per continuare a ricevere events KEYCODE_DEL, anche senza TYPE_NULL. Vedi questo AOSP commit alla row 1493:

    https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/android-4.1.1_r1/java/src/com/android/inputmethod/latin/LatinIME.java

    Pertanto, è ansible risolvere questo problema impostando targetSdkVersion nell'applicazione a 15 o inferiore.

    A partire dal commit 4.4_r0.9 (appena prima della versione 4.4), questo problema è stato risolto aggiungendo un test per isTypeNull () alle condizioni che proteggono la generazione KEYCODE_DEL. Purtroppo, un nuovo bug (62306) è stato introdotto esattamente in quel punto che ha causato l'integer clausola che avvolge la generazione di KEYCODE_DEL per essere saltato se l'utente avesse digitato backspace il numero di volte che aveva digitato altri caratteri. Ciò ha causato una mancanza di generazione di KEYCODE_DEL in queste circostanze, anche con TYPE_NULL e persino con targetSdkVersion <= 15. Ciò ha causato le applicazioni precedentemente in grado di get correttamente il comportmento KEYCODE_DEL tramite codice di compatibilità (targetSdkVersion <= 15) problema quando gli utenti hanno aggiornato le loro copie di Google Keyboard (o eseguito un OTA che conteneva una nuova versione di Google Keyboard). Vedi questo file GIT AOSP alla row 2146 (la clausola che include "NOT_A_CODE"):

    https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/android-4.4_r0.9/java/src/com/android/inputmethod/latin/LatinIME.java

    Questo problema è persistito nelle versioni rilasciate di Google Keyboard fino al momento attuale (1/7/2014). È stato fissato nel repo, ma da questa scrittura non è stato rilasciato.

    Questo commit non pubblicato può essere trovato qui (il commit di git che contiene questo fonde un commit denominato "Invia backspace come evento quando TYPE_NULL") alla row 2110 (si può vedere che la clausola "NOT_A_CODE" che usava per impedire di raggiungere la clausola che genera KEYCODE_DEL è stato rimosso):

    https://android.googlesource.com/platform/packages/inputmethods/LatinIME/+/b41bea65502ce7339665859d3c2c81b4a29194e4/java/src/com/android/inputmethod/latin/LatinIME.java

    Quando questa correzione viene rilasciata, la versione di Google Keyboard non avrà più uno dei due problemi che interessano TYPE_NULL. Tuttavia , le versioni precedenti verranno installate su dispositivi specifici per un futuro indefinito. Di conseguenza, il problema richiederà ancora una soluzione. Infine, poiché molte persone aggiornano ad un livello superiore rispetto all'ultimo senza includere la correzione, questa soluzione sarà necessaria sempre less. Ma è già capace di eliminare la fase (una volta effettuate le modifiche indicate per mettere il limite finale sull'ambito, quando la correzione finale è stata effettivamente rilasciata in modo da sapere che cosa è in realtà).

    Sembra un bug con Android:

    Numero 42904: evento KEYCODE_DEL non viene consegnato a EditText in SDK 16 e versioni successive.

    Numero 42904 @ code.google.com

    INTRODUZIONE:

    Dopo aver testato le soluzioni di @ Carl e @ Turix ho notato che:

    1. La soluzione di Carl non funziona bene con caratteri unicode o sequenze di caratteri, in quanto sembrano essere consegnati con l'evento ACTION_MULTIPLE, che rende difficile distinguere i caratteri "dummy" e il carattere reale.

    2. Non sono riuscito a get deleteSurroundingText funziona nell'ultima versione di Android sul mio Nexus 5 (4.4.2). Ho provato a specificare diverse versioni di sdk diverse, ma nessuno di loro ha funzionato. Forse Google ha deciso di cambiare ancora una volta la logica dietro il tasto DEL …

    Pertanto, ho trovato la seguente soluzione combinata, utilizzando le risposte di Carl e Turix. La mia soluzione funziona combinando l'idea di Carl di un lungo prefisso di carattere fittizio per fare il lavoro DEL, ma utilizzando la soluzione di Turix per una personalizzazione Editable per generare events corretti.

    RISULTATI:

    Ho provato questa soluzione su diversi dispositivi con diverse versioni di Android e diverse tastiere. Tutti i casi di prova qui sotto funzionano per me. Non ho trovato un caso in cui questa soluzione non funziona.

    • Nexus 5 (4.4.2) con la tastiera standard di Google
    • Nexus 5 (4.4.2) con SwiftKey
    • HTC One (4.2.2) con la tastiera standard HTC
    • Nexus One (2.3.6) con la tastiera standard di Google
    • Samsung Galaxy S3 (4.1.2) con la tastiera standard Samsung

    Ho anche testato il targeting di diverse versioni SDK:

    • Target 16
    • Target 19

    Se questa soluzione funziona anche per te, allora ple

    LA VISTA:

     public class MyInputView extends EditText implements View.OnKeyListener { private String DUMMY; ... public MyInputView(Context context) { super(context); init(context); } private void init(Context context) { this.context = context; this.setOnKeyListener(this); // Generate a dummy buffer string // Make longer or shorter as desired. DUMMY = ""; for (int i = 0; i < 1000; i++) DUMMY += "\0"; } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Override public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode characters (even character sequeneces) // But make sure we aren't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getCharacters(); if (!s.equals(DUMMY)) { listener.onSend(s); } } // Catch key presses... if (action == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_DEL: ... break; case KeyEvent.KEYCODE_ENTER: ... break; case KeyEvent.KEYCODE_TAB: ... break; default: char ch = (char)keyEvent.getUnicodeChar(); if (ch != '\0') { ... } break; } } return false; } } 

    COLLEGAMENTO INGRESSO:

     public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View targetView, boolean fullEditor) { super(targetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(CharSequence source) { super(source); } @Override public SpannableStringBuilder replace(final int start, final int end, CharSequence tb, int tbstart, int tbend) { if (tbend > tbstart) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstart, tbend); } else if (end > start) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(start, end, tb, tbstart, tbend); } } @Override public Editable getEditable() { if (Build.VERSION.SDK_INT < 14) return super.getEditable(); if (mEditable == null) { mEditable = this.new MyEditable(DUMMY); Selection.setSelection(mEditable, DUMMY.length()); } else if (mEditable.length() == 0) { mEditable.append(DUMMY); Selection.setSelection(mEditable, DUMMY.length()); } return mEditable; } @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android version... return super.deleteSurroundingText(beforeLength, afterLength); } } 

    Ho affrontato problemi simili in cui KEYCODE_DEL non è stato ricevuto sul touch di chiave backspace. Dipende dalla tastiera Soft Input penso, perché il mio problema stava succedendo solo in caso di tastiere di terze parti (swype penso) e non con la tastiera google predefinita.

    (Questa risposta è intesa come un addendum alla risposta accettata posta qui da Carl.)

    Pur apprezzando molto la ricerca e la comprensione dei due bug, ho avuto qualche difficoltà con la soluzione proposta qui da Carl. Il problema principale che ho avuto è che, sebbene il block di commento di Carl spiega che il path KeyEvent.ACTION_MULTIPLE in onKey() verrà considerato solo "il primo evento ricevuto dopo aver selezionato il rack"; per me, each singolo evento chiave ha preso quel path . (Ho scoperto guardando il codice BaseInputConnection.java per il livello API-18 che questo è perché tutto il text Editable viene utilizzato in sendCurrentText() each volta. Non so perché ha funzionato per Carl, ma non me.)

    Così, ispirato alla soluzione di Carl, l'ho adattato per non avere questo problema. La mia soluzione per emettere 62306 (legata alla risposta di Carl) cerca di get lo stesso effetto fondamentale di "trionfare" l'IME nel pensare che ci sia sempre più text che può essere spostato indietro. Tuttavia, questo fa in modo che l'Editable abbia esattamente un carattere in esso. A tal fine, è necessario estendere la class sottostante che implementa l'interface Editable , SpannedStringBuilder , in un modo simile a quanto segue:

      private class MyEditable extends SpannableStringBuilder { MyEditable(CharSequence source) { super(source); } @Override public SpannableStringBuilder replace(final int start, final int end, CharSequence tb, int tbstart, int tbend) { if (tbend > tbstart) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstart, tbend); } else if (end > start) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(start, end, tb, tbstart, tbend); } } 

    Fondamentalmente, each volta che l'IME tenta di aggiungere un carattere all'Editable (chiamando replace() ), quel carattere sostituisce qualsiasi carattere singolo è presente. Nel frattempo, se l'IME tenta di rimuovere ciò che c'è, replace() sostituire sostituisce quello che c'è con un carattere singleton "dummy" (che dovrebbe essere qualcosa che l'applicazione ignorerà) per mantenere la lunghezza di 1.

    Ciò significa che le implementazioni di getEditable() e onKey() possono essere leggermente più semplici di quanto Carl descritto sopra. Ad esempio, assumendo che la class MyEditable sopra è implementata come class interna, getEditable() diventa qualcosa di simile:

      @Override public Editable getEditable() { if (Build.VERSION.SDK_INT < 14) return super.getEditable(); if (mEditable == null) { mEditable = this.new MyEditable(DUMMY_CHAR); Selection.setSelection(mEditable, 1); } else if (m_editable.length() == 0) { mEditable.append(DUMMY_CHAR); Selection.setSelection(mEditable, 1); } return mEditable; } 

    Si noti che con questa soluzione non è necessario mantenere una string lunga 1024 caratteri. Né, c'è il pericolo di "spostarsi troppo" (come discusso nei commenti di Carl su come tenere premuto il tasto backspace).

    Per completezza, onKey() diventa qualcosa di simile:

      @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) return false; if ((int)DUMMY_CHAR.charAt(0) == event.getUnicodeChar()) return true; // Handle event/keyCode here as normal... } 

    Infine, vorrei notare che tutto quanto sopra è inteso come una soluzione per emettere solo 62306. Non ho avuto problemi con la soluzione all'altro problema, 42904, come inviato da Carl (override deleteSurroundingText() ) e lo consiglierei di utilizzare come ha postato.

    A causa dei pensieri di Carl, sono venuto a una soluzione che funziona correttamente per qualsiasi tipo di input. Di seguito fornisco un'applicazione completa di esempio di lavoro composta da 2 classi: MainActivity e CustomEditText :

     package com.example.edittextbackspace; import android.app.Activity; import android.os.Bundle; import android.text.InputType; import android.view.ViewGroup.LayoutParams; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CustomEditText edittext = initEditText(); setContentView(edittext); } private CustomEditText initEditText() { CustomEditText editText = new CustomEditText(this) { @Override public void backSpaceProcessed() { super.backSpaceProcessed(); editTextBackSpaceProcessed(this); } }; editText.setInputType(InputType.TYPE_CLASS_NUMBER); editText.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); editText.setText("1212"); return editText; } private void editTextBackSpaceProcessed(CustomEditText customEditText) { // Backspace event is called and properly processed } } 

     package com.example.edittextbackspace; import android.content.Context; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.EditText; import java.util.ArrayList; import java.util.List; public class CustomEditText extends EditText implements View.OnFocusChangeListener, TextWatcher { private String LOG = this.getClass().getName(); private int _inputType = 0; private int _imeOptions = 5 | EditorInfo.IME_FLAG_NO_EXTRACT_UI; private List<String> _lastComposingTextsList = new ArrayList<String>(); private BaseInputConnection _inputConnection = null; private String _lastComposingText = ""; private boolean _commitText = true; private int _lastCursorPosition = 0; private boolean _isComposing = false; private boolean _characterRemoved = false; private boolean _isTextComposable = false; public CustomEditText(Context context) { super(context); setOnFocusChangeListener(this); addTextChangedListener(this); } @Override public InputConnection onCreateInputConnection(final EditorInfo outAttrs) { CustomEditText.this._inputConnection = new BaseInputConnection(this, false) { @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { handleEditTextDeleteEvent(); return super.deleteSurroundingText(beforeLength, afterLength); } @Override public boolean setComposingText(CharSequence text, int newCursorPosition) { CustomEditText.this._isTextComposable = true; CustomEditText.this._lastCursorPosition = getSelectionEnd(); CustomEditText.this._isComposing = true; if (text.toString().equals(CustomEditText.this._lastComposingText)) return true; else CustomEditText.this._commitText = true; if (text.length() < CustomEditText.this._lastComposingText.length()) { CustomEditText.this._lastComposingText = text.toString(); try { if (text.length() > 0) { if (CustomEditText.this._lastComposingTextsList.size() > 0) { if (CustomEditText.this._lastComposingTextsList.size() > 0) { CustomEditText.this._lastComposingTextsList.remove(CustomEditText.this._lastComposingTextsList.size() - 1); } } else { CustomEditText.this._lastComposingTextsList.add(text.toString().substring(0, text.length() - 1)); } } int start = Math.max(getSelectionStart(), 0) - 1; int end = Math.max(getSelectionEnd(), 0); CustomEditText.this._characterRemoved = true; getText().replace(Math.min(start, end), Math.max(start, end), ""); } catch (Exception e) { Log.e(LOG, "Exception in setComposingText: " + e.toString()); } return true; } else { CustomEditText.this._characterRemoved = false; } if (text.length() > 0) { CustomEditText.this._lastComposingText = text.toString(); String textToInsert = Character.toString(text.charAt(text.length() - 1)); int start = Math.max(getSelectionStart(), 0); int end = Math.max(getSelectionEnd(), 0); CustomEditText.this._lastCursorPosition++; getText().replace(Math.min(start, end), Math.max(start, end), textToInsert); CustomEditText.this._lastComposingTextsList.add(text.toString()); } return super.setComposingText("", newCursorPosition); } @Override public boolean commitText(CharSequence text, int newCursorPosition) { CustomEditText.this._isComposing = false; CustomEditText.this._lastComposingText = ""; if (!CustomEditText.this._commitText) { CustomEditText.this._lastComposingTextsList.clear(); return true; } if (text.toString().length() > 0) { try { String stringToReplace = ""; int cursorPosition = Math.max(getSelectionStart(), 0); if (CustomEditText.this._lastComposingTextsList.size() > 1) { if (text.toString().trim().isEmpty()) { getText().replace(cursorPosition, cursorPosition, " "); } else { stringToReplace = CustomEditText.this._lastComposingTextsList.get(CustomEditText.this._lastComposingTextsList.size() - 2) + text.charAt(text.length() - 1); getText().replace(cursorPosition - stringToReplace.length(), cursorPosition, text); } CustomEditText.this._lastComposingTextsList.clear(); return true; } else if (CustomEditText.this._lastComposingTextsList.size() == 1) { getText().replace(cursorPosition - 1, cursorPosition, text); CustomEditText.this._lastComposingTextsList.clear(); return true; } } catch (Exception e) { Log.e(LOG, "Exception in commitText: " + e.toString()); } } else { if (!getText().toString().isEmpty()) { int cursorPosition = Math.max(getSelectionStart(), 0); CustomEditText.this._lastCursorPosition = cursorPosition - 1; getText().replace(cursorPosition - 1, cursorPosition, text); if (CustomEditText.this._lastComposingTextsList.size() > 0) { CustomEditText.this._lastComposingTextsList.remove(CustomEditText.this._lastComposingTextsList.size() - 1); } return true; } } return super.commitText(text, newCursorPosition); } @Override public boolean sendKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); CustomEditText.this._lastComposingTextsList.clear(); if (keyCode > 60 && keyCode < 68 || !CustomEditText.this._isTextComposable || (CustomEditText.this._lastComposingTextsList != null && CustomEditText.this._lastComposingTextsList.size() == 0)) { return super.sendKeyEvent(event); } else return false; } @Override public boolean finishComposingText() { if (CustomEditText.this._lastComposingTextsList != null && CustomEditText.this._lastComposingTextsList.size() > 0) CustomEditText.this._lastComposingTextsList.clear(); CustomEditText.this._isComposing = true; CustomEditText.this._commitText = true; return super.finishComposingText(); } @Override public boolean commitCorrection(CorrectionInfo correctionInfo) { CustomEditText.this._commitText = false; return super.commitCorrection(correctionInfo); } }; outAttrs.actionLabel = null; outAttrs.inputType = this._inputType; outAttrs.imeOptions = this._imeOptions; return CustomEditText.this._inputConnection; } @Override public boolean onKeyDown(int keyCode, KeyEvent keyEvent) { if (keyCode == KeyEvent.KEYCODE_DEL) { int cursorPosition = this.getSelectionEnd() - 1; if (cursorPosition < 0) { removeAll(); } } return super.onKeyDown(keyCode, keyEvent); } @Override public void setInputType(int type) { CustomEditText.this._isTextComposable = false; this._inputType = type; super.setInputType(type); } @Override public void setImeOptions(int imeOptions) { this._imeOptions = imeOptions | EditorInfo.IME_FLAG_NO_EXTRACT_UI; super.setImeOptions(this._imeOptions); } public void handleEditTextDeleteEvent() { int end = Math.max(getSelectionEnd(), 0); if (end - 1 >= 0) { removeChar(); backSpaceProcessed(); } else { removeAll(); } } private void removeAll() { int startSelection = this.getSelectionStart(); int endSelection = this.getSelectionEnd(); if (endSelection - startSelection > 0) this.setText(""); else nothingRemoved(); } private void removeChar() { KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); super.onKeyDown(event.getKeyCode(), event); } public void nothingRemoved() { // Backspace didn't remove anything. It means, a cursor of the editText was in the first position. We can use this method, for example, to switch focus to a previous view } public void backSpaceProcessed() { // Backspace is properly processed } @Override protected void onSelectionChanged(int selStart, int selEnd) { if (CustomEditText.this._isComposing) { int startSelection = this.getSelectionStart(); int endSelection = this.getSelectionEnd(); if (((CustomEditText.this._lastCursorPosition != selEnd && !CustomEditText.this._characterRemoved) || (!CustomEditText.this._characterRemoved && CustomEditText.this._lastCursorPosition != selEnd)) || Math.abs(CustomEditText.this._lastCursorPosition - selEnd) > 1 || Math.abs(endSelection - startSelection) > 1) { // clean autoprediction words CustomEditText.this._lastComposingText = ""; CustomEditText.this._lastComposingTextsList.clear(); CustomEditText.super.setInputType(CustomEditText.this._inputType); } } } @Override public void onFocusChange(View v, boolean hasFocus) { if (!hasFocus) { CustomEditText.this._lastComposingText = ""; CustomEditText.this._lastComposingTextsList.clear(); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { int startSelection = getSelectionStart(); int endSelection = getSelectionEnd(); if (Math.abs(endSelection - startSelection) > 0) { Selection.setSelection(getText(), endSelection); } } @Override public void afterTextChanged(Editable s) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { super.onTextChanged(s, start, before, count); } } 

    UPDATE: I updated the code, because it was not working properly when Text Prediction is enabled on some devices like Samsung Galaxy S6 (thanks @Jonas that he informsd about this issue in the comment below) and using InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS doesn't help in this case. I tested this solution on lots of devices, but still not sure whether it works properly for all. I hope I will get some reviews from you in case of any improper behaviour of the EditText.

    I think you may find that you can intercept the key if you override the dispatchKeyEvent method of the appropriate view/activity (in my case, the main activity was fine).

    For example I'm developing an app for a device which has hardware scroll keys, and I was surprised to discover the onKeyUp / onKeyDown methods never get called for them. Instead, by default the key press goes through a bunch of dispatchKeyEvent s until it invokes a scroll method somewhere (in my case, bizarrely enough, one key press invokes a scroll methods on each of two separate scrollable views–how annoying).

    What if you checked for like the decimal number for the backspace character?

    I think its like '/r' (decimal number 7) or something, at least for ASCII.

    EDIT: I guess Android uses UTF-8, so this decimal number would be 8. http://www.fileformat.info/info/unicode/char/0008/index.htm

    InputFilter called for backspace and if edittext is empty.

     editText.setFilters(new InputFilter[]{new InputFilter() { @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { if(source.equals("")) { //a backspace was entered } return source; } }}); 

    Given the response by Umair you may consider applying a workaround here:

    Capture a touch event that it's NOT a key event and happens around the lower-right part of the screen while the keyboard is shown.

    Come get la posizione di Touch in android?

    C'è un modo per sapere se è mostrata la tastiera soft?

    Spero possa aiutare

    This is old post and giving my suggestions in case somebody is in need of super quick hack/implementation.

    The simplest work around I came out with is to implement TextWatcher too along with on OnKeyListener and in onTextChanged compare with the previous existing string whether it is reduced by one character.

    The benefit of this is it works on any type of keyboard with no long coding process easily.

    For instance my editText holds only one character, so I compared characterSequence if it is empty string, then by that we can acknowledge that Delete key is pressed.

    Below is the code explaining the same:

     @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { if(charSequence.toString().equals("")) //Compare here for any change in existing string by single character with previous string { //Carry out your tasks here it comes in here when Delete Key is pressed. } } 

    Note: In this case my edittext contains only single character so I'm comparing charSequesnce with empty string(since pressing delete will make it empty), for your needs you need to modify it and compare(Like after pressing key substring is part of the original string) it with existing string. Spero che sia d'aiuto.

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.