Android 获取软键盘的删除delete事件

宁可枝头抱香死,何曾吹落北风中。这篇文章主要讲述Android 获取软键盘的删除delete事件相关的知识,希望能为你提供帮助。
对于软键盘删除事件,网上有很多方案是如下,但是 google api也说明了,这个只是监听硬件键盘,对于软键盘并不负责触发(我测试了一下,软键盘能够监听delete键,其他键像数字字母等没有触发这里的监听方法)。
 

editText.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { //You can identify which key pressed buy checking keyCode value with KeyEvent.KEYCODE_ if(keyCode == KeyEvent.KEYCODE_DEL) { //this is for backspace } return false; } });

【Android 获取软键盘的删除delete事件】 
当然,也有是通过TextWatcher来处理delete事件,但是这个监听只在数据变化时才触发,如果edittext本身就没有内容,此时点击软件盘delete键也就不会触发这里的方法。
类似于下面的逻辑:
 
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { mPreviousLength = s.length(); }@Override public void onTextChanged(CharSequence s, int start, int before, int count) { }@Override public void afterTextChanged(Editable s) { mBackSpace = mPreviousLength > s.length(); if (mBackSpace) {// do your stuff ...} }

 
 
其实对于软件盘的监听,还要从源头找起,这里先介绍一下自定义view输入
android之自定义View来接收输入法输入的内容对于很多新人来讲,能接收输入法输入的内容大概只有EditText和TextView这两个控件了,其实不然,只要是View的子类,都可以接收输入法输入的内容。
现在我们一步一步来实现,第一步我们得有一个View的子类。
[html] view plain copy print?
  1. //首先我们得重写View中的一个方法,返回true,就是让这个View变成文本可编辑的状态,默认返回false。     
  2. @Override  public  boolean  onCheckIsTextEditor()  {     
  3.         return  true;      
  4.         }     
  5.         //第二个就是重写方法,需要返回一个InputConnect对象,这个是和输入法输入内容的桥梁。   
  6. public  InputConnection  onCreateInputConnection(EditorInfo  outAttrs);   
  7.    
  8. //  outAttrs就是我们需要设置的输入法的各种类型最重要的就是:   
  9. outAttrs.imeOptions  =  EditorInfo.IME_FLAG_NO_EXTRACT_UI;   outAttrs.inputType  =  InputType.TYPE_NULL;    
//首先我们得重写View中的一个方法,返回true,就是让这个View变成文本可编辑的状态,默认返回false。 @Override public boolean onCheckIsTextEditor() {       return true;       }       //第二个就是重写方法,需要返回一个InputConnect对象,这个是和输入法输入内容的桥梁。 public InputConnection onCreateInputConnection(EditorInfo outAttrs);// outAttrs就是我们需要设置的输入法的各种类型最重要的就是: outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI; outAttrs.inputType = InputType.TYPE_NULL;


这里我只是随便设置,重要的是返回的InputConnect对象。以下是
[html] view plain copy print?
  1. InputConnection     
InputConnection

需要重写的方法
 
[java] view plain copy print?
  1. //一般我们都是些一个BaseInputConnection的子类,而BaseInputConnection是实现了InputConnection接口的。   
  2.    
  3. 需要注意的就是几个方法注意重写。   
  4.    
  5. @Override   
  6. public  boolean  commitText(CharSequence  text,  int  newCursorPosition)  {     
  7.           Log.d("hickey",  "commitText:"  +  text  +  "\t"  +  newCursorPosition);    
  8.           if  (containsEmoji(text.toString()))  {     
  9.                 Log.d("hickey",  "send  emoji");      
  10.                 return  true;    
  11.           }     
  12.           if  (mPlayer  !=  null  & &   mPlayFragment.isInputMethodStatus())  {   
  13.                     Log.d("hickey",  "text:"  +  text);      
  14.                     mPlayerView.sendCharEvent(text.toString());    
  15.           }     
  16.         return  true;    
  17.   }   
  18.    
  19. note:这个是当输入法输入了字符,包括表情,字母、文字、数字和符号。我们可以通过text筛选出我们不想让显示到自定义view上面。   
  20.    
  21. //有文本输入,当然也有按键输入,也别注意的是有些输入法输入数字并非用commitText方法传递,而是用按键来代替,比如KeyCode_1是代表1等。   
  22.    
  23.         @Override   
  24.         public  boolean  sendKeyEvent(KeyEvent  event)  {   
  25.                 /**  当手指离开的按键的时候  */   
  26.                 if  (event.getAction()  ==  KeyEvent.ACTION_DOWN)  {   
  27.                         Log.d("hickey",  "sendKeyEvent:KeyCode="  +  event.getKeyCode());    
  28.                         if  (event.getKeyCode()  ==  KeyEvent.KEYCODE_DEL)  {   
  29.                                 mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_DEL);    
  30.                         }  else  if  (event.getKeyCode()  ==  KeyEvent.KEYCODE_ENTER)  {   
  31.                                 mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_ENTER);    
  32.                                 mPlayFragment.setInputMethodStatus(false,  1);    
  33.                         }  else  {   
  34.                                 mPlayerView.sendCharKeyCodeEvent(event.getKeyCode());    
  35.                         }   
  36.                 }   
  37.                 return  true;    
  38.         }   
  39.    
  40. note:这里我只做了删除,回车按键的处理,由于会触发动作按下和松开两次,所以在这里只做了按下的处理。   
  41.    
  42. //当然删除的时候也会触发   
  43. @Override   
  44.         public  boolean  deleteSurroundingText(int  beforeLength,  int  afterLength)  {   
  45.                 Log.d("hickey",  "deleteSurroundingText  "  +  "beforeLength="  +  beforeLength  +  "  afterLength="  +  afterLength);    
  46.                 mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_DEL);    
  47.                 return  true;    
  48.         }   
  49.    
  50.   @Override   
  51.         public  boolean  finishComposingText()  {   
  52.                 //结束组合文本输入的时候   
  53.                 Log.d("hickey",  "finishComposingText");    
  54.                 return  true;    
  55.         }   
  56. //这个方法基本上会出现在切换输入法类型,点击回车(完成、搜索、发送、下一步)点击输入法右上角隐藏按钮会触发。   
  57.    
  58. 这里引申出多个问题,比如说当我们点击View上的时候,需要弹出输入法咋办?   
  59. 我们可以通过InputMethodManager来控制输入法弹起和缩回。   
  60.    
  61.         InputMethodHelper(Context  mContext)  {   
  62.                 inputMethodManager  =  (InputMethodManager)  mContext.getSystemService(Context.INPUT_METHOD_SERVICE);    
  63.         }   
  64.    
  65.         public  synchronized  static  InputMethodHelper  getInstance(Context  mContext)  {   
  66.                 synchronized  (InputMethodHelper.class)  {   
  67.                         if  (inputMethodHelper  ==  null)  {   
  68.                                 inputMethodHelper  =  new  InputMethodHelper(mContext);    
  69.                         }   
  70.                         return  inputMethodHelper;    
  71.                 }   
  72.         }   
  73.         /** 
  74.           *  显示软键盘 
  75.           * 
  76.           *  @param  view 
  77.           */   
  78.         public  void  showSoftInput(View  view)  {   
  79.                 inputMethodManager.showSoftInput(view,  0);    
  80.         }   
  81.    
  82.         /** 
  83.           *  隐藏输入法 
  84.           */   
  85.         public  void  hideSoftInput(View  view)  {   
  86.                 if  (inputMethodManager.isActive())  {   
  87.                         Log.d("hickey",  "hideSoftInput:"  +  "hideSoftInputFromWindow");    
  88.                         inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(),  0);    
  89.                 }   
  90.         }   
  91.    
  92. 在非全屏状态下,我们可以通过布局大小的改变来监听输入法的弹起和缩回,但是在全屏状态下呢,抱歉,目前是不可以的。比如说用户点击了输入法的隐藏按钮,只会触发finishComposingText这个方法,但是其他时候也会触发此方法,所以想通过此方法监听输入法缩回是不可行的,InputMethodManager也没有提供相关的API,试过获取IMM的提供的   
  93.    
  94.         public  boolean  isActive(View  view){   
  95.                 return  inputMethodManager.isActive(view);    
  96.         }   
  97.    
  98.         public  boolean  isActive(){   
  99.                 return  inputMethodManager.isActive();    
  100.         }   
  101.    
  102.         public  boolean  isWatchingCursor  (View  view){   
  103.                 return  inputMethodManager.isWatchingCursor(view);    
  104.         }   
  105.    
  106.         public  boolean  isAcceptingText(){   
  107.                 return  inputMethodManager.isAcceptingText();    
  108.         }   
  109.    
  110.      
//一般我们都是些一个BaseInputConnection的子类,而BaseInputConnection是实现了InputConnection接口的。需要注意的就是几个方法注意重写。@Override public boolean commitText(CharSequence text, int newCursorPosition) { Log.d("hickey", "commitText:" + text + "\t" + newCursorPosition); if (containsEmoji(text.toString())) { Log.d("hickey", "send emoji"); return true; } if (mPlayer != null & & mPlayFragment.isInputMethodStatus()) { Log.d("hickey", "text:" + text); mPlayerView.sendCharEvent(text.toString()); } return true; }note:这个是当输入法输入了字符,包括表情,字母、文字、数字和符号。我们可以通过text筛选出我们不想让显示到自定义view上面。//有文本输入,当然也有按键输入,也别注意的是有些输入法输入数字并非用commitText方法传递,而是用按键来代替,比如KeyCode_1是代表1等。@Override public boolean sendKeyEvent(KeyEvent event) { /** 当手指离开的按键的时候 */ if (event.getAction() == KeyEvent.ACTION_DOWN) { Log.d("hickey", "sendKeyEvent:KeyCode=" + event.getKeyCode()); if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_DEL); } else if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) { mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_ENTER); mPlayFragment.setInputMethodStatus(false, 1); } else { mPlayerView.sendCharKeyCodeEvent(event.getKeyCode()); } } return true; }note:这里我只做了删除,回车按键的处理,由于会触发动作按下和松开两次,所以在这里只做了按下的处理。板面//当然删除的时候也会触发 @Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { Log.d("hickey", "deleteSurroundingText " + "beforeLength=" + beforeLength + " afterLength=" + afterLength); mPlayerView.sendFunctionKeyCodeEvent(KeyEvent.KEYCODE_DEL); return true; } @Override public boolean finishComposingText() { //结束组合文本输入的时候 Log.d("hickey", "finishComposingText"); return true; } //这个方法基本上会出现在切换输入法类型,点击回车(完成、搜索、发送、下一步)点击输入法右上角隐藏按钮会触发。这里引申出多个问题,比如说当我们点击View上的时候,需要弹出输入法咋办? 我们可以通过InputMethodManager来控制输入法弹起和缩回。InputMethodHelper(Context mContext) { inputMethodManager = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); }public synchronized static InputMethodHelper getInstance(Context mContext) { synchronized (InputMethodHelper.class) { if (inputMethodHelper == null) { inputMethodHelper = new InputMethodHelper(mContext); } return inputMethodHelper; } } /** * 显示软键盘 * * @param view */ public void showSoftInput(View view) { inputMethodManager.showSoftInput(view, 0); }/** * 隐藏输入法 */ public void hideSoftInput(View view) { if (inputMethodManager.isActive()) { Log.d("hickey", "hideSoftInput:" + "hideSoftInputFromWindow"); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } }在非全屏状态下,我们可以通过布局大小的改变来监听输入法的弹起和缩回,但是在全屏状态下呢,抱歉,目前是不可以的。比如说用户点击了输入法的隐藏按钮,只会触发finishComposingText这个方法,但是其他时候也会触发此方法,所以想通过此方法监听输入法缩回是不可行的,InputMethodManager也没有提供相关的API,试过获取IMM的提供的public boolean isActive(View view){ return inputMethodManager.isActive(view); }public boolean isActive(){ return inputMethodManager.isActive(); }public boolean isWatchingCursor (View view){ return inputMethodManager.isWatchingCursor(view); }public boolean isAcceptingText(){ return inputMethodManager.isAcceptingText(); }


都没有任何成效。

还有一种情况是当前Activity退出了,输入法还健在,且输入了没有任何内容。而且我们试过所有隐藏输入法的方法,都无法正常的隐藏输入法。

这里告诉告诉大家一个比较贱的方法,在输入法健在的时候,我们点击返回按钮,都会主动隐藏输入法,再次点击才会把按键事件分发传递到Activity上。

所以,我们就需要模拟一个返回的事件。


[java] view plain copy print?
  1. new  Thread(new  Runnable()  {   
  2.                                 @Override   
  3.                                 public  void  run()  {   
  4.                                         RedFinger.simulationEvent  =  true;    
  5.                                         Instrumentation  instrumentation  =  new  Instrumentation();    
  6.                                         instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);    
  7.                                 }   
  8.                         }).start();    
new Thread(new Runnable() { @Override public void run() { RedFinger.simulationEvent = true; Instrumentation instrumentation = new Instrumentation(); instrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); } }).start();



//这里弄了个bool标志是防止输入已经隐藏还会分发返回按键事件到Activity上,所以需要在可能退出到的页面上
 
 
 
看了这里后,你就可以了解怎么获取软键盘的事件了。
在stackoverflow上也有很多讨论,我找到了两篇文章
https://stackoverflow.com/questions/4886858/android-edittext-deletebackspace-key-event
https://stackoverflow.com/questions/18581636/android-cannot-capture-backspace-delete-press-in-soft-keyboard/34857618#34857618
这里面找到了一个不错的解决方案,重写edittext,代码如下
 
[java] view plain copy print?
  1. public  class  WiseEditText  extends  AppCompatEditText  {       
  2.        
  3.        
  4.         private  OnKeyListener  keyListener;        
  5.        
  6.         public  WiseEditText(Context  context,  AttributeSet  attrs,  int  defStyle)  {       
  7.                 super(context,  attrs,  defStyle);        
  8.         }       
  9.        
  10.         public  WiseEditText(Context  context,  AttributeSet  attrs)  {       
  11.                 super(context,  attrs);        
  12.         }       
  13.        
  14.         public  WiseEditText(Context  context)  {       
  15.                 super(context);        
  16.         }       
  17.        
  18.         @Override       
  19.         public  InputConnection  onCreateInputConnection(EditorInfo  outAttrs)  {       
  20.                 return  new  MyInputConnection(super.onCreateInputConnection(outAttrs),       
  21.                                 true);        
  22.         }       
  23.        
  24.         private  class  MyInputConnection  extends  InputConnectionWrapper  {       
  25.        
  26.                 public  MyInputConnection(InputConnection  target,  boolean  mutable)  {       
  27.                         super(target,  mutable);        
  28.                 }       
  29.        
  30.                 @Override       
  31.                 public  boolean  sendKeyEvent(KeyEvent  event)  {       
  32.                         if  (keyListener  !=  null)  {       
  33.                                 keyListener.onKey(WiseEditText.this,event.getKeyCode(),event);        
  34.                         }       
  35.                         return  super.sendKeyEvent(event);        
  36.                 }       
  37.        
  38.                 @Override       
  39.                 public  boolean  deleteSurroundingText(int  beforeLength,  int  afterLength)  {                     
  40.                         //  magic:  in  latest  Android,  deleteSurroundingText(1,  0)  will  be  called  for  backspace       
  41.                         if  (beforeLength  ==  1  & &   afterLength  ==  0)  {       
  42.                                 //  backspace       
  43.                                 return  sendKeyEvent(new  KeyEvent(KeyEvent.ACTION_DOWN,  KeyEvent.KEYCODE_DEL))       
  44.                                         & &   sendKeyEvent(new  KeyEvent(KeyEvent.ACTION_UP,  KeyEvent.KEYCODE_DEL));        
  45.                         }       
  46.        
  47.                         return  super.deleteSurroundingText(beforeLength,  afterLength);        
  48.                 }       
  49.        
  50.         }       
  51.        
  52.       //设置监听回调       
  53.         public  void  setSoftKeyListener(OnKeyListener  listener){       
  54.                 keyListener  =  listener;        
  55.         }       
  56.        
  57. }   
public class WiseEditText extends AppCompatEditText {private OnKeyListener keyListener; public WiseEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }public WiseEditText(Context context, AttributeSet attrs) { super(context, attrs); }public WiseEditText(Context context) { super(context); }@Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { return new MyInputConnection(super.onCreateInputConnection(outAttrs), true); }private class MyInputConnection extends InputConnectionWrapper {public MyInputConnection(InputConnection target, boolean mutable) { super(target, mutable); }@Override public boolean sendKeyEvent(KeyEvent event) { if (keyListener != null) { keyListener.onKey(WiseEditText.this,event.getKeyCode(),event); } return super.sendKeyEvent(event); }@Override public boolean deleteSurroundingText(int beforeLength, int afterLength) { // magic: in latest Android, deleteSurroundingText(1, 0) will be called for backspace if (beforeLength == 1 & & afterLength == 0) { // backspace return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) & & sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); }return super.deleteSurroundingText(beforeLength, afterLength); }}//设置监听回调 public void setSoftKeyListener(OnKeyListener listener){ keyListener = listener; }}




 
 
[java] view plain copy print?
    1. < pre  snippet_file_name="blog_20170805_5_3934361"  code_snippet_id="2519238"> < /pre>    
    2. < pre> < /pre>    
    3. < pre> < /pre>    
    4.      















    推荐阅读