【问题标题】:Detect changes in EditText (TextWatcher ineffective)检测EditText的变化(TextWatcher无效)
【发布时间】:2012-03-16 14:23:22
【问题描述】:

我需要检测 EditText 中的文本更改。我已经尝试过 TextWatcher,但它并没有以我期望的方式工作。取 onTextChanged 方法:

public void onTextChanged( CharSequence s, int start, int before, int count )

假设我已经在 EditText 中有文本“John”。如果按另一个键,“e”,s 将是“Johne”,start 将是 0,before 将是 4,count 将是 5。我希望这种方法的工作方式是是以前的 EditText 和它将成为的区别。

所以我希望:

s = "Johne"
start = 4 // inserting character at index = 4
before = 0 // adding a character, so there was nothing there before
count = 1 // inserting one character

我需要能够在每次按下某个键时检测到单个更改。因此,如果我有文本“John”,我需要知道在索引 4 处添加了“e”。如果我退格“e”,我需要知道从索引 4 中删除了“e”。如果我将光标放在“J”之后" 和退格键,我需要知道 "J" 已从索引 0 中删除。如果我在 "J" 所在的位置放置 "G",我想知道 "G" 在索引 0 处替换了 "J"。

我怎样才能做到这一点?我想不出一个可靠的方法来做到这一点。

【问题讨论】:

  • 为 EditText 尝试 onKeyListener
  • 从剪贴板粘贴怎么样?
  • 我遇到的另一个问题是说我选择了一个文本范围。在任何 TextWatcher 方法中,getSelectionStart 和 End 始终是相同的索引,无论我是否选择了文本。
  • @jason 你是对的:当输入单个字符数必须为 1 时,你是如何设置 TextWatcher 的?以及您使用的是什么 API 级别?
  • @pskink 这是一个旧项目,很遗憾我无法再访问。我不记得我是如何设置 TextWatcher 的。对不起。

标签: android android-edittext textwatcher


【解决方案1】:

使用 textwatcher 并自己进行 diff。将先前的文本存储在观察者中,然后将先前的文本与您在 onTextChanged 获得的任何序列进行比较。由于 onTextChanged 在每个字符之后都会触发,因此您知道之前的文本和给定的文本最多会相差一个字母,这应该可以很容易地找出在哪里添加或删除了哪个字母。即:

new TextWatcher(){ 
    String previousText = theEditText.getText();

    @Override 
    onTextChanged(CharSequence s, int start, int before, int count){
        compare(s, previousText); //compare and do whatever you need to do
        previousText = s;
    }

    ...
}

【讨论】:

  • 这是我考虑过的建议,当然可行,但我希望简单的“给你!”方法,可能不存在。您的方法的唯一问题是文本可能相差超过 1 个字母(选择范围,从剪贴板粘贴)。
  • 是的,但是此时索引没有什么意义(例如,如果您在当前文本之前粘贴一个单词会发生什么?),即使那样您仍然可以自己进行差异。我认为没有任何内置解决方案可以解决您正在尝试做的事情。
  • 很公平。我最终按照你所说的做了,基本上实现了我自己的 TextWatcher 并传入了我期望的值。这不是 100% 万无一失,但非常接近。
【解决方案2】:

识别文本更改的最佳方法。

var previousText = ""


override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    previousText = s.toString()
}

override fun onTextChanged(newText: CharSequence?, start: Int, before: Int, count: Int) {

    val position = start + before ;

    if(newText!!.length > previousText.length){ //Character Added
        Log.i("Added Character", " ${newText[position]} ")
        Log.i("At Position", " $position ")
    } else {  //Character Removed
        Log.i("Removed Character", " ${previousText[position-1]} ")
        Log.i("From Position", " ${position-1} ")
    }
}

override fun afterTextChanged(finalText: Editable?) { }

【讨论】:

    【解决方案3】:

    每次更改文本时,您都需要存储和更新之前的 CharSequence。您可以通过实现TextWatcher 来做到这一点。

    例子:

    final CharSequence[] previousText = {""};
    editText.addTextChangedListener(new TextWatcher()
    {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
        {
        }
    
        @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
        {
            if(i1 > 0)
            {
                System.out.println("Removed Chars Positions & Text:");
                for(int index = 0; index < i1; index++)
                {
                    System.out.print((i + index) + " : " + previousText[0].charAt(i + index)+", ");
                }
            }
            if(i2 > 0)
            {
                System.out.println("Inserted Chars Positions & Text:");
                for(int index = 0; index < i2; index++)
                {
                    System.out.print((index + i) + " : " + charSequence.charAt(i + index)+", ");
                }
                System.out.print("\n");
            }
            previousText[0] = charSequence.toString();//update reference
        }
    
        @Override public void afterTextChanged(Editable editable)
        {
        }
    });
    

    【讨论】:

      【解决方案4】:

      我最近遇到了完全相同的问题,我编写了自己的自定义算法来检测 TextWatcher 输出中的差异。

      算法-

      我们存储 4 样东西 -

      1. 旧的选择大小
      2. 旧文本
      3. 光标/选择之前的旧文本序列。
      4. 光标/选择后的旧文本序列。

      beforeTextChanged() 回调期间更新了以上 4 项内容。

      现在在 onTextChanged() 回调期间,我们计算以下两件事 -

      1. 光标/选择之前的新文本序列。
      2. 光标/选择后的新文本序列。

      现在可能出现以下情况 -

      案例 1

      New text sequence before the cursor/selection == Old text sequence before the cursor/selectionNew text sequence after the cursor/selection isASuffixOf Old text sequence after the cursor/selection

      这是一个删除转发案例。删除字符数可以通过oldText长度减去newText长度来计算。

      例子-

      旧文本 = Hello wo|rld(| 代表光标)

      光标/选择之前的旧文本序列 = Hello wo

      光标/选择后的旧文本序列 = rld

      旧选择大小 = 0

      新文本 = Hello wo|ld(| 代表光标)

      光标/选择之前的新文本序列 = Hello wo

      光标/选择后的新文本序列 = ld

      显然,这是向前删除 1 个字符的情况。

      案例 2

      New text sequence after the cursor/selection == Old text sequence after the cursor/selectionNew text sequence before the cursor/selection isAPrefixOf Old text sequence before the cursor/selection

      这是一个向后删除的情况。删除字符数可以通过oldText长度减去newText长度来计算。

      例子-

      旧文本 = Hello wo|rld(| 代表光标)

      光标/选择之前的旧文本序列 = Hello wo

      光标/选择后的旧文本序列 = rld

      旧选择大小 = 0

      新文本 = Hello w|rld(| 代表光标)

      光标/选择之前的新文本序列 = Hello w

      光标/选择后的新文本序列 = rld

      显然,这是向后删除1个字符的情况。

      案例 3

      New text sequence after the cursor/selection == Old text sequence after the cursor/selectionOld text sequence before the cursor/selection isAPrefixOf New text sequence before the cursor/selection

      这是一个插盒。可以通过从新文本字符串中删除old text sequence from cursor + old text sequence after cursor 来计算确切的插入字符串。

      例子-

      旧文本 = Hello wo|rld(| 代表光标)

      光标/选择之前的旧文本序列 = Hello wo

      光标/选择后的旧文本序列 = rld

      旧选择大小 = 0

      新文本 = Hello wo123|rld(| 代表光标)

      光标/选择之前的新文本序列 = Hello wo123

      光标/选择后的新文本序列 = rld

      显然,这是插入的情况,插入的字符串是123

      案例 4

      如果上述情况都不满足,那么我们可以说它是一个替换情况。并且替换数据已经由 TextWatcher 在 onTextChanged 回调中提供。

      这是上述算法的代码-

      
      class MyTextWatcher : android.text.TextWatcher {
      
              var oldSelectionSize = 0
              var oldText: String = ""
              var oldSequenceBeforeCursor: String = ""
              var oldSequenceAfterCursor: String = ""
      
              override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
      
                  oldSelectionSize = editText.selectionEnd - editText.selectionStart
                  oldText = s.toString()
                  oldSequenceBeforeCursor = s?.subSequence(0, editText.selectionStart).toString()
                  oldSequenceAfterCursor = s?.subSequence(editText.selectionEnd, s.length).toString()
              }
      
              override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
      
                  s?.toString()?.let { newText ->
                      val newSequenceBeforeCursor = newText.subSequence(0, selectionStart).toString()
                      val newSequenceAfterCursor = newText.subSequence(selectionEnd, newText.length)
                              .toString()
      
                      if (newSequenceBeforeCursor == oldSequenceBeforeCursor &&
                              oldSequenceAfterCursor.endsWith(newSequenceAfterCursor))
                          // handle delete forward 
                          // number of characters to delete ==>
                          // if(oldSelectionSize > 0) then deleted number of characters = oldSelectionSize
                          // else number of characters to delete = oldText.length - newText.length
                      else if (newSequenceAfterCursor == oldSequenceAfterCursor &&
                              oldSequenceBeforeCursor.startsWith(newSequenceBeforeCursor))
                          // handle delete backward 
                          // number of characters to delete ==>
                          // if(oldSelectionSize > 0) then deleted number of characters = oldSelectionSize
                          // else number of characters to delete = oldText.length - newText.length
                      else if (newSequenceAfterCursor == oldSequenceAfterCursor &&
                              newSequenceBeforeCursor.startsWith(oldSequenceBeforeCursor))
                          // handle insert
                          // inserted string = (newText - oldSequenceBeforeCursor) - oldSequenceAfterCursor
                      else
                          // handle replace
                          // replace info already provided in `onTextChanged()` arguments.
                  }
              }
      
              override fun afterTextChanged(s: Editable?) {
              }
          }
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-04
        • 2011-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多