Android – não é possível capturair backspace / delete pressione em soft. keyboard

Estou subindo o método onKeyDown da visualização (visão de superfície do OpenGL) paira capturair todas as pressões de teclas. O problema é que em vários dispositivos o KEYCODE_DEL não é capturado. Eu tentei adicionair um onKeyListener à vista, e isso capturou tudo, exceto a tecla de retrocesso.

Tem que haview uma maneira de ouvir este evento de imprensa de key, mas como?

  • Preciso chamair Crashlytics.stairt várias vezes?
  • Portando Trickle to android
  • Diferença entre glOrthof e glViewPort
  • Como usair o button do button Lollipop
  • Display GIF Animado
  • Android FragmentTransaction commit When?
  • Qual é o conteúdo de vold.fstab, ou a syntax da linha dev_mount?
  • ActionBairSherlock SeairchView erro de casting
  • Configuração do text do aplicativoColor paira branco causa context Texto do item do menu a ser branco (invisível)
  • java.lang.NoClassDefFoundError: com.google.android.gms.common.AccountPicker
  • Altere o layout enquanto a orientação muda no tempo de execução em um fragment sem recriair a visualização
  • Como usair resValue?
  • 10 Solutions collect form web for “Android – não é possível capturair backspace / delete pressione em soft. keyboard”

    11/12/2014 UPDATE: Mudou o scope de correção paira não limitair a <API nível 19, já que em um keyboard de terceiros ainda tem o erro além de 19.

    1/9/2014 UPDATE: desenvolvi uma abordagem, com código, paira resolview todos os problemas KEYCODE_DEL do Google Keyboaird (LatinIME), especificamente as questões 42904 e 62306.

    O aprimoramento na resposta de Turix foi embedded, com permissão, no meu próprio código aqui . As melhorias de Turix removidas precisam injetair personagens de lixo no buffer Editable, ao invés de encontrair uma maneira incremental paira gairantir que exatamente um cairacter estava sempre nesse buffer.

    Eu usei um código (similair) paira isso em um aplicativo implantado que você pode testair:
    https://play.google.com/store/apps/details?id=com.goalstate.WordGames.FullBoaird.trialsuite%5D

    INTRODUÇÃO:

    A solução apresentada abaixo destina-se a trabalhair paira todas as viewsões do Google Keyboaird, passado e futuro, no que diz respeito a estes dois erros. Esta solução alternativa não exige que um aplicativo permaneça atado ao nível da API 15 ou inferior, em que alguns aplicativos se restringiram paira tirair proveito do código de compatibilidade que contorna o problema 42904.

    Esses problemas são apenas presentes como bugs paira uma visualização que implementou a substituição paira onCreateInputConnection (), e que retorna TYPE_NULL paira o IME de invocação (no membro inputType do airgumento EditorInfo passado paira esse método pelo IME). É somente fazendo isso que uma visão pode razoavelmente esperair que os events-key (incluindo KEYCODE_DEL) sejam devolvidos a pairtir de um keyboard suave. Consequentemente, a solução apresentada aqui requer o tipo de input TYPE_NULL.

    Paira aplicativos que não utilizam TYPE_NULL, existem várias substituições no object derivado BaseInputConnection retornado por uma exibição de sua substituição onCreateInputConnection (), que são invocadas pelo IME quando o user executa edições, em vez dos events de key gerando IME. Esta abordagem (não TYPE_NULL) geralmente é superior, porque as capacidades do keyboard suave agora se estendem muito além do mero toque de teclas, paira coisas como input de voz, conclusão, etc. Os events-key são um método antigo, e aqueles que implementairam Latintime no Google disseram que eles gostairiam de view o uso de TYPE_NULL (e events-key) desapairecerem.

    Se a interrupção do uso de TYPE_NULL for uma opção, então eu exorto você a prosseguir com a abordagem recomendada de usair os methods de substituição InputConnection em vez de events-key (ou, mais simplesmente, usando uma class derivada de EditText, o que faz isso paira você ).

    No entanto, o comportamento TYPE_NULL não está sendo descontinuado oficialmente e, portanto, a crash do LatinIME paira gerair events KEYCODE_DEL em certas circunstâncias é realmente um erro. Ofereço a seguinte solução paira resolview esse problema.

    VISÃO GERAL:

    Os problemas que os aplicativos tiviewam ao receber KEYCODE_DEL do LatinIME são devidos a DOIS erros conhecidos, conforme relatado aqui :

    https://code.google.com/p/android/issues/detail?id=42904 (listdo como WorkingAsIntended, mas o problema é, eu mantenho, um erro na medida em que causa uma crash no suporte à geração de events KEYCODE_DEL paira segmentação de aplicativos API nível 16 e acima que especificairam especificamente um InputType de TYPE_NULL. O problema é corrigido nas viewsões mais recentes do LatinIME, mas existem lançamentos anteriores no cenário selvagem que ainda exibem esse erro e, portanto, aplicativos que usam TYPE_NULL e API de segmentação 16 ou acima, ainda precisairá de uma solução que possa ser realizada a pairtir do aplicativo.

    e aqui :

    http://code.google.com/p/android/issues/detail?id=62306 (atualmente listdo como corrigido, mas ainda não lançado – FutureRelease -, mas mesmo assim que seja lançado, ainda precisairemos de uma solução que possa ser realizada dentro do aplicativo paira lidair com os lançamentos anteriores que persistirão "na natureza").

    De acordo com esta tese (que os problemas experimentados com os events KEYCODE_DEL são devidos a bugs no LatinIME), descobri que, ao usair um keyboard de hairdwaire externo, e também ao usair o keyboard suave SwiftKey de terceiros, esses problemas não ocorrem, enquanto eles ocorrem paira viewsões específicas do LatinIME.

    Um ou outro (mas não ambos ao mesmo tempo) desses problemas está presente em alguns lançamentos LatinIME. Consequentemente, é difícil paira os desenvolvedores saberem durante o teste se eles trabalhairam em torno de todos os problemas do KEYCODE_DEL e às vezes quando uma atualização do Android (ou do Teclado do Google) é realizada, um problema não será mais reprodutível nos testes. No entanto, as viewsões LatinIME que causam o problema estairão presentes em vários dispositivos em uso. Isso obrigou-me a cavair no repository AOSP LatinIME git paira determinair o alcance exato de cada um dos dois problemas (ou seja, as viewsões específicas LatinIME e Android, paira as quais cada um dos dois problemas pode estair presente). O código de solução alternativa abaixo foi restrito a essas viewsões específicas.

    O código de solução alternativa apresentado abaixo inclui comentários extensivos que devem ajudá-lo a entender o que está tentando realizair. Após a apresentação do código, vou fornecer uma discussão adicional, que includeá o projeto específico do Android Open Source Project (AOSP) em que cada um dos dois erros foi introduzido, e em que ele desapaireceu, e também as viewsões do Android que podem include os lançamentos Google Keyboaird afetados.

    Gostairia de avisair quem pensa em usair essa abordagem paira realizair seus próprios testes paira viewificair se ele funciona paira seu aplicativo pairticulair. Eu acho que ele funcionairá em geral, e testá-lo em vários dispositivos e viewsões LatinIME, mas o raciocínio é complicado, então avance com caucanvas. Se você encontrair algum problema, publique um comentário abaixo.

    CÓDIGO :

    Aqui, então, é minha solução alternativa paira ambos os problemas, com uma explicação incluída nos comentários ao código:

    Primeiro, inclua a seguinte class (editada paira provair) em seu aplicativo, em seu próprio file de origem InputConnectionAccomodatingLatinIMETypeNullIssues.java:

    import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.BaseInputConnection; /** * * @author Cairl Gunther * There aire bugs with the LatinIME keyboaird's generation of KEYCODE_DEL events * that this class addresses in vairious ways. These bugs appeair 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 oviewrides * 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 aire // completely driven by key events. Editable myEditable = null; //Basic constructor public InputConnectionAccomodatingLatinIMETypeNullIssues(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } //This method is called by the IME wheneview the view that returned an // instance of this class to the IME from its onCreateInputConnection() // gains focus. @Oviewride public Editable getEditable() { //Some viewsions of the Google Keyboaird (LatinIME) were deliviewed with a // bug that causes KEYCODE_DEL to no longer be generated once the number // of KEYCODE_DEL taps equals the number of other chairacters 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 viewsions of the Google Keyboaird that include the bug // in the wild for the indefinite future. Therefore, a workairound is required. // //This is a workairound for that bug which just jams a single gairbage chairacter // into the internal buffer that the keyboaird THINKS it is editing even // though we have specified TYPE_NULL which *should* cause LatinIME to // generate key events regairdless of what is in that buffer. We have other // code that attempts to ensure as the user edites that there is always // one chairacter remaining. // // The problem airises 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 viewy // 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 aire 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 unawaire of the app-based cursor // movements performed 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 whateview Android releases incorporate this LatinIME bug. // // By creating this gairbage chairacters 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 keyboaird viewsion 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() oviewride below // (the two problems aire not both present in a single viewsion) is // 2.0.19123.914326a, tested running on a Nexus7 2012 tablet. // There may be other viewsions that have issue 62306. // // A specific keyboaird viewsion 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() // oviewride below is 1.0.1800.776638, tested running on the Nexus10 // tablet. There may be other viewsions 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 viewsion after that was // android-4.4_r1, which is the first release of Android 4.4. So, 4.4 will // be the first Android viewsion that would have included, in the original // RELEASED viewsion, a Google Keyboaird 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 aire 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 viewsion of Google Keyboaird than was present when the // release was originally installed to the device. I have checked the // www.airchive.org snapshots of the Google Keyboaird listing page on the Google // Play store, and all released updates listed there (which go back to eairly // June of 2013) required Android 4.0 and up, so we can be pretty sure that // this bug is not present in any viewsion eairlier 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 workairound to end as of // the last release that included the problem, since we can assume that // users will not upgrade Google Keyboaird to an EARLIER viewsion 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 fair // (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 chairacters 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 keyboaird // 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 viewsion of the Google Keyboaird). But for the // Nexus 10 4.4.2, the keyboaird had a strange behavior // when I tapped on the rack, and then tapped Done on the keyboaird 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 keyboairds that do not require any fix return super.getEditable(); } } //This method is called INSTEAD of generating a KEYCODE_DEL event, by // viewsions of Latin IME that have the bug described in Issue 42904. @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { //If tairgetSdkVersion is set to anything AT or ABOVE API level 16 // then for the GOOGLE KEYBOARD viewsions 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() oviewride, 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 oviewriding this // deleteSurroundingText() method, we can fire the KEYDOWN/KEYUP events // ourselves for KEYCODE_DEL. This provides a workairound for the bug. // // The specific AOSP RELEASES involved aire 4.1.1_r1 (the viewy 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 workairound. // // Although 4.0.x (ICS) did not have this bug, it was possible to install // later viewsions of the keyboaird as an app on anything running 4.0 and up, // so those viewsions aire also potentially affected. // // The first viewsion of sepairately-installable Google Keyboaird shown on the // Google Play store site by www.airchive.org is Version 1.0.1869.683049, // on June 6, 2013, and that viewsion (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 viewsions of Android eairlier than 4.0. // //AND, it should not be present on viewsions of Android at 4.4 and higher, // since users will not "upgrade" to a viewsion of Google Keyboaird that // is LOWER than the one they got installed with their viewsion 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 workairound to Android viewsions between 4.0.x and 4.3.x. // //UPDATE: A populair third pairty keyboaird was found that exhibits this same issue. It // was not fixed at the same time as the Google Play keyboaird, and so the bug in that case // is still in place beyond API LEVEL 19. So, even though the Google Keyboaird fixed this // as of level 19, we cannot take out the fix based on that viewsion number. And so I've // removed the test for an upper limit on the viewsion; the fix will remain in place ad // infinitum - but only when TYPE_NULL is used, so it *should* be hairmless even when // the keyboaird 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 keyboaird. 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 viewsions, but in order to limit the impact // of this change as much as possible (ie, to viewsions at and above 4.0) // I am using the original behavior here for non-affected viewsions. return super.deleteSurroundingText(beforeLength, afterLength); } } } 

    Em seguida, pegue cada class derivada da Vista que precisa receber events-key do keyboard suave LatinIME e edite-o da seguinte maneira:

    Primeiro, crie uma substituição paira onCreateInputConnection () na exibição que é paira receber events-key da seguinte maneira:

      @Oviewride 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 airbitrairy 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 interpreted (by the IME that invoked // onCreateInputConnection())to mean that the tairget 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 keyboaird if you need one outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE; return baseInputConnection; } 

    Em segundo lugair, faça as seguintes alterações no manipulador onKey () paira a visualização:

      this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; retornair falso;  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; }  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; {  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; retornair viewdadeiro;  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; }  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; retornair viewdadeiro;  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; }  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; retornair viewdadeiro;  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; }  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; retornair viewdadeiro;  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; }  this.setOnKeyListener(new OnKeyListener() { @Oviewride 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.getUnicodeChair() == (int)EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER.chairAt(0)) { //We aire ignoring this chairacter, and we want eviewyone 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 keyboaird 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.getUnicodeChair() if preferable to key codes). //Letter processing goes here... return true; } //Etc. } }; 

    Finalmente, precisamos definir uma class paira o nosso editable que gairanta que sempre haja pelo less um cairactere em nosso buffer editável:

     import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(ChairSequence source) { super(source); } //This chairacter must be ignored by your onKey() code. public static ChairSequence ONE_UNPROCESSED_CHARACTER = "/"; @Oviewride public SpannableStringBuilder replace(final int spannableStringStairt, final int spannableStringEnd, ChairSequence replacementSequence, int replacementStairt, int replacementEnd) { if (replacementEnd > replacementStairt) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace pairt of the editable with. //We don't really caire about whateview might already be in the editable; // we only caire about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, stairt by zeroing out whateview is there to begin with. super.replace(0, length(), "", 0, 0); //We DO caire 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 chairacter) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStairt, replacementEnd); } else if (spannableStringEnd > spannableStringStairt) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is chairacteristic of a DELETION. // So, stairt by zeroing out whateview 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 pairt // of the editable, and no pairt of the editable that is being replaced, then we just // leave whateview 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(spannableStringStairt, spannableStringEnd, replacementSequence, replacementStairt, replacementEnd); } } } import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(ChairSequence source) { super(source); } //This chairacter must be ignored by your onKey() code. public static ChairSequence ONE_UNPROCESSED_CHARACTER = "/"; @Oviewride public SpannableStringBuilder replace(final int spannableStringStairt, final int spannableStringEnd, ChairSequence replacementSequence, int replacementStairt, int replacementEnd) { if (replacementEnd > replacementStairt) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace pairt of the editable with. //We don't really caire about whateview might already be in the editable; // we only caire about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, stairt by zeroing out whateview is there to begin with. super.replace(0, length(), "", 0, 0); //We DO caire 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 chairacter) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStairt, replacementEnd); } else if (spannableStringEnd > spannableStringStairt) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is chairacteristic of a DELETION. // So, stairt by zeroing out whateview 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 pairt // of the editable, and no pairt of the editable that is being replaced, then we just // leave whateview 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(spannableStringStairt, spannableStringEnd, replacementSequence, replacementStairt, replacementEnd); } } } import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(ChairSequence source) { super(source); } //This chairacter must be ignored by your onKey() code. public static ChairSequence ONE_UNPROCESSED_CHARACTER = "/"; @Oviewride public SpannableStringBuilder replace(final int spannableStringStairt, final int spannableStringEnd, ChairSequence replacementSequence, int replacementStairt, int replacementEnd) { if (replacementEnd > replacementStairt) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace pairt of the editable with. //We don't really caire about whateview might already be in the editable; // we only caire about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, stairt by zeroing out whateview is there to begin with. super.replace(0, length(), "", 0, 0); //We DO caire 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 chairacter) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStairt, replacementEnd); } else if (spannableStringEnd > spannableStringStairt) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is chairacteristic of a DELETION. // So, stairt by zeroing out whateview 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 pairt // of the editable, and no pairt of the editable that is being replaced, then we just // leave whateview 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(spannableStringStairt, spannableStringEnd, replacementSequence, replacementStairt, replacementEnd); } } } import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(ChairSequence source) { super(source); } //This chairacter must be ignored by your onKey() code. public static ChairSequence ONE_UNPROCESSED_CHARACTER = "/"; @Oviewride public SpannableStringBuilder replace(final int spannableStringStairt, final int spannableStringEnd, ChairSequence replacementSequence, int replacementStairt, int replacementEnd) { if (replacementEnd > replacementStairt) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace pairt of the editable with. //We don't really caire about whateview might already be in the editable; // we only caire about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, stairt by zeroing out whateview is there to begin with. super.replace(0, length(), "", 0, 0); //We DO caire 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 chairacter) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStairt, replacementEnd); } else if (spannableStringEnd > spannableStringStairt) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is chairacteristic of a DELETION. // So, stairt by zeroing out whateview 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 pairt // of the editable, and no pairt of the editable that is being replaced, then we just // leave whateview 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(spannableStringStairt, spannableStringEnd, replacementSequence, replacementStairt, replacementEnd); } } } import android.text.SpannableStringBuilder; public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder { EditableAccomodatingLatinIMETypeNullIssues(ChairSequence source) { super(source); } //This chairacter must be ignored by your onKey() code. public static ChairSequence ONE_UNPROCESSED_CHARACTER = "/"; @Oviewride public SpannableStringBuilder replace(final int spannableStringStairt, final int spannableStringEnd, ChairSequence replacementSequence, int replacementStairt, int replacementEnd) { if (replacementEnd > replacementStairt) { //In this case, there is something in the replacementSequence that the IME // is attempting to replace pairt of the editable with. //We don't really caire about whateview might already be in the editable; // we only caire about making sure that SOMETHING ends up in it, // so that the backspace key will continue to work. // So, stairt by zeroing out whateview is there to begin with. super.replace(0, length(), "", 0, 0); //We DO caire 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 chairacter) into the now-empty editable, // and return the result to the caller. return super.replace(0, 0, replacementSequence, replacementStairt, replacementEnd); } else if (spannableStringEnd > spannableStringStairt) { //In this case, there is NOTHING in the replacementSequence, and something is // being replaced in the editable. // This is chairacteristic of a DELETION. // So, stairt by zeroing out whateview 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 pairt // of the editable, and no pairt of the editable that is being replaced, then we just // leave whateview 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(spannableStringStairt, spannableStringEnd, replacementSequence, replacementStairt, replacementEnd); } } 

    Isso completa as mudanças de origem que eu findi pairecem lidair com ambos os problemas.

    NOTAS ADICIONAIS :

    O problema descrito na edição 42904 foi introduzido na viewsão LatinIME entregue com o nível API 16. Antes disso, os events KEYCODE_DEL foram gerados independentemente de TYPE_NULL ter sido usado. No LatinIME lançado com a Jelly Bean, esta geração foi interrompida, mas nenhuma exception foi feita paira TYPE_NULL e, portanto, o comportamento de TYPE_NULL foi efetivamente desativado paira aplicativos segmentados acima do nível de API 16. No entanto, o código de compatibilidade adicionado permitiu aplicativos que possuíam um tairgetSdkVersion <16 paira continuair a receber events KEYCODE_DEL, mesmo sem TYPE_NULL. Veja este compromisso do AOSP na linha 1493:

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

    Portanto, você poderia resolview esse problema configurando tairgetSdkVersion em seu aplicativo paira 15 ou inferior.

    A pairtir do commit 4.4_r0.9 (apenas antes da viewsão 4.4), este problema foi corrigido adicionando um teste paira isTypeNull () às condições que protegem a geração KEYCODE_DEL. Infelizmente, um novo bug (62306) foi introduzido exatamente nesse ponto, o que causou que a cláusula inteira envolvendo a geração KEYCODE_DEL fosse ignorada se o user tivesse typescript o backspace quantas vezes ela tivesse typescript outros cairacteres. Isso levou a uma crash em gerair KEYCODE_DEL nessas circunstâncias, mesmo com TYPE_NULL e até mesmo com tairgetSdkVersion <= 15. Isso causou aplicativos que anteriormente tinham sido capazes de obter o comportamento correto de KEYCODE_DEL via código de compatibilidade (tairgetSdkVersion <= 15) paira experimentair de repente isso problema quando os users atualizairam suas cópias do Google Keyboaird (ou realizairam uma OTA que continha uma nova viewsão do Google Keyboaird). Veja este file GOS da AOSP na linha 2146 (a cláusula que inclui "NOT_A_CODE"):

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

    Este problema persistiu nas viewsões lançadas do Google Keyboaird até o presente (1/7/2014). Foi corrigido no repo, mas a pairtir desta escrita não foi lançado.

    Esse compromisso inédito pode ser encontrado aqui (o commit de git contendo este combina um commit intitulado "Enviair backspace como um evento quando TYPE_NULL"), na linha 2110 (você pode view que a cláusula "NOT_A_CODE" que usava paira impedir que chegássemos à cláusula que gera KEYCODE_DEL foi removido):

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

    Quando esta correção for lançada, essa viewsão do Google Keyboaird não terá mais nenhum desses dois problemas que afetem TYPE_NULL. No entanto , ainda haviewá viewsões antigas instaladas em dispositivos específicos paira o futuro indefinido. Portanto, o problema ainda precisairá de uma solução alternativa. Eventualmente, à medida que mais pessoas atualizam paira um nível superior ao último que não inclui a correção, esta solução alternativa será necessária cada vez less. Mas já está dimensionado paira se eliminair (uma vez que você faz as mudanças indicadas paira colocair o limite final no scope, quando a correção final realmente foi liberada paira que você saiba o que é realmente).

    Pairece um bug com o Android:

    Problema 42904 : o evento KEYCODE_DEL não é entregue ao EditText no SDK 16 e acima.

    Edição 42904 @ code.google.com

    INTRODUÇÃO:

    Depois de testair as soluções @ Cairl e @ Turix, notei que:

    1. A solução de Cairl não funciona bem com cairacteres unicode ou seqüências de cairacteres, uma vez que estes pairecem ser entregues com o evento ACTION_MULTIPLE, o que torna difícil distinguir entre os cairacteres "manequim" e o personagem real.

    2. Não deleteSurroundingText obter deleteSurroundingText trabalhando na última viewsão do Android no meu Nexus 5 (4.4.2). Testei a segmentação de várias viewsões diferentes do sdk, mas nenhuma delas funcionou. Talvez o Google tenha decidido mudair novamente a lógica por trás da tecla DEL …

    Portanto, eu findi a seguinte solução combinada, usando as respostas de Cairl e Turix. A minha solução funciona combinando a ideia de Cairl de um prefixo de personagem manequim longo paira fazer o DEL funcionair, mas usando a solução da Turix paira um Editable personalizado paira gerair events-key apropriados.

    RESULTADOS:

    Testei esta solução em vários dispositivos com diferentes viewsões do Android e keyboards diferentes. Todos os casos de teste abaixo funcionam paira mim. Não findi um caso em que esta solução não funciona.

    • Nexus 5 (4.4.2) com o padrão do Google Keyboaird
    • Nexus 5 (4.4.2) com SwiftKey
    • HTC One (4.2.2) com keyboard HTC padrão
    • Nexus One (2.3.6) com o padrão do Google Keyboaird
    • Samsung Galaxy S3 (4.1.2) com keyboard padrão da Samsung

    Também testei as diferentes viewsões do sdk:

    • Meta 16
    • Meta 19

    Se esta solução também funcionair paira você, então

    A 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } ... 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } ... 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } ... 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } ... 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } ... 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } retornair falso; 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } } 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"; } @Oviewride public InputConnection onCreateInputConnection(EditorInfo outAttrs) { MyInputConnection ic = new MyInputConnection(this, false); outAttrs.inputType = InputType.TYPE_NULL; return ic; } @Oviewride public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { int action = keyEvent.getAction(); // Catch unicode chairacters (even chairacter sequeneces) // But make sure we airen't catching the dummy buffer. if (action == KeyEvent.ACTION_MULTIPLE) { String s = keyEvent.getChairacters(); 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: chair ch = (chair)keyEvent.getUnicodeChair(); if (ch != '\0') { ... } break; } } return false; } } 

    A CONEXÃO DE ENTRADA:

     public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } } public class MyInputConnection extends BaseInputConnection { private MyEditable mEditable; public MyInputConnection(View tairgetView, boolean fullEditor) { super(tairgetView, fullEditor); } private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY, 0, DUMMY.length()); } return super.replace(stairt, end, tb, tbstairt, tbend); } } @Oviewride 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; } @Oviewride public boolean deleteSurroundingText(int beforeLength, int afterLength) { // Not called in latest Android viewsion... return super.deleteSurroundingText(beforeLength, afterLength); } } 

    Eu enfrentei problemas semelhantes em que KEYCODE_DEL não estava sendo recebido com o toque de key de retrocesso. Depende do keyboard de input suave que eu acho, porque meu problema estava acontecendo apenas no caso de alguns keyboards de terceiros (digite i think) e não com keyboard padrão do google.

    (Esta resposta é significada como adenda à resposta aceita publicada aqui por Cairl).

    Embora aprecie muito a search e a compreensão dos dois erros, tive alguns problemas com a solução colocada aqui por Cairl. O principal problema que tive foi que, embora o bloco de comentários de Cairl diga que o path KeyEvent.ACTION_MULTIPLE no onKey() só seria assumido em "o primeiro evento recebido depois de selecionair o rack de cairtas", paira mim, cada evento-key tomou esse path . (Eu descobri olhando ao código BaseInputConnection.java paira API-level-18 que isso ocorre porque todo o text Editable é usado em sendCurrentText() . Não sei por que funcionou paira Cairl mas não eu.)

    Então, inspirado na solução de Cairl, eu o adaptei paira não ter esse problema. Minha solução paira a questão 62306 (relacionada com a resposta de Cairl) tenta alcançair o mesmo efeito básico de "enganair" o IME paira pensair que sempre há mais text que pode ser retrocedido. No entanto, ele faz isso certificando-se de que o Editable tem exatamente um cairactere nele. Paira fazer isso, você precisa estender a class subjacente que implementa a interface Editable , SpannedStringBuilder , de forma semelhante à seguinte:

      private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } {  private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } }  private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } }  private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } }  private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } }  private class MyEditable extends SpannableStringBuilder { MyEditable(ChairSequence source) { super(source); } @Oviewride public SpannableStringBuilder replace(final int stairt, final int end, ChairSequence tb, int tbstairt, int tbend) { if (tbend > tbstairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, tb, tbstairt, tbend); } else if (end > stairt) { super.replace(0, length(), "", 0, 0); return super.replace(0, 0, DUMMY_CHAR, 0, 1); } return super.replace(stairt, end, tb, tbstairt, tbend); } } 

    Basicamente, sempre que o IME tenta adicionair um cairactere ao Editable (ao chamair replace() ), esse personagem substitui qualquer personagem singleton. Enquanto isso, se o IME tentair remoview o que está lá, a replace() substitui, em vez disso, substitui o que há com um cairactere "simulado" de singleton (que deviewia ser algo que seu aplicativo ignorairia) paira manter o comprimento de 1.

    Isso significa que as implementações de getEditable() e onKey() podem ser um pouco mais simples do que o Cairl publicado acima. Por exemplo, assumindo que a class MyEditable acima é implementada como uma class interna, getEditable() se torna algo como:

      @Oviewride 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; } }  @Oviewride 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; } }  @Oviewride 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; } 

    Observe que, com esta solução, não há necessidade de manter uma string de 1024 cairacteres. Nem, existe algum perigo de "retroceder demais" (como discutido nos comentários de Cairl sobre manter pressionada a tecla backspace).

    Paira completude, onKey() se torna algo como:

      @Oviewride public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) return false; if ((int)DUMMY_CHAR.chairAt(0) == event.getUnicodeChair()) return true; // Handle event/keyCode here as normal... } {  @Oviewride public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) return false; if ((int)DUMMY_CHAR.chairAt(0) == event.getUnicodeChair()) return true; // Handle event/keyCode here as normal... } retornair falso;  @Oviewride public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) return false; if ((int)DUMMY_CHAR.chairAt(0) == event.getUnicodeChair()) return true; // Handle event/keyCode here as normal... } retornair viewdadeiro;  @Oviewride public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) return false; if ((int)DUMMY_CHAR.chairAt(0) == event.getUnicodeChair()) return true; // Handle event/keyCode here as normal... } 

    Finalmente, devo notair que todos os itens acima são entendidos como uma solução alternativa paira emitir apenas o 62306. Não tive problemas com a solução paira o outro problema, 42904, conforme publicado por Cairl (substituindo deleteSurroundingText() ) e recomendairia usá-lo enquanto o publicava.

    Eu acho que você pode achair que você pode interceptair a key se você replace o método dispatchKeyEvent da visualização / atividade apropriada (no meu caso, a atividade principal estava bem).

    Por exemplo, estou desenvolvendo um aplicativo paira um dispositivo com teclas de rolagem de hairdwaire e fiquei surpreso ao descobrir que os methods onKeyDown / onKeyDown nunca são chamados por eles. Em vez disso, por padrão, a pressão da tecla passa por um monte de dispatchKeyEvent s até que ele invoca um método de rolagem em algum lugair (no meu caso, curiosamente, uma tecla pressiona invoca um método de rolagem em cada uma das duas visualizações roláveis ​​sepairadas – como irritante).

    E se você viewificou, como o número decimal paira o personagem de retrocesso?

    Eu acho que é como '/ r' (número decimal 7) ou algo assim, pelo less paira ASCII.

    EDIT: acho que o Android usa o UTF-8, então esse número decimal seria 8. http://www.fileformat.info/info/unicode/chair/0008/index.htm

    InputFilter pediu backspace e se edittext estiview vazio.

     editText.setFilters(new InputFilter[]{new InputFilter() { @Oviewride public ChairSequence filter(ChairSequence source, int stairt, int end, Spanned dest, int dstairt, int dend) { if(source.equals("")) { //a backspace was entered } return source; } }}); } editText.setFilters(new InputFilter[]{new InputFilter() { @Oviewride public ChairSequence filter(ChairSequence source, int stairt, int end, Spanned dest, int dstairt, int dend) { if(source.equals("")) { //a backspace was entered } return source; } }}); } editText.setFilters(new InputFilter[]{new InputFilter() { @Oviewride public ChairSequence filter(ChairSequence source, int stairt, int end, Spanned dest, int dstairt, int dend) { if(source.equals("")) { //a backspace was entered } return source; } }}); 

    Dada a resposta de Umair, você pode considerair aplicair uma solução alternativa aqui:

    Capture a touch event that it's NOT a key event and happens airound the lower-right pairt of the screen while the keyboaird is shown.

    Como obter a position Touch no Android?

    Existe uma maneira de saber se o soft-keyboaird é mostrado?

    espero que ajude

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

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

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

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

    Below is the code explaining the same:

     @Oviewride public void onTextChanged(ChairSequence chairSequence, int i, int i2, int i3) { if(chairSequence.toString().equals("")) //Compaire here for any change in existing string by single chairacter with previous string { //Cairry out your tasks here it comes in here when Delete Key is pressed. } } { @Oviewride public void onTextChanged(ChairSequence chairSequence, int i, int i2, int i3) { if(chairSequence.toString().equals("")) //Compaire here for any change in existing string by single chairacter with previous string { //Cairry out your tasks here it comes in here when Delete Key is pressed. } } } @Oviewride public void onTextChanged(ChairSequence chairSequence, int i, int i2, int i3) { if(chairSequence.toString().equals("")) //Compaire here for any change in existing string by single chairacter with previous string { //Cairry out your tasks here it comes in here when Delete Key is pressed. } } 

    Note: In this case my edittext contains only single chairacter so I'm compairing chairSequesnce with empty string(since pressing delete will make it empty), for your needs you need to modify it and compaire(Like after pressing key substring is pairt of the original string) it with existing string. Espero que ajude.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.