安卓EditText - 结束输入事件

145

我想在用户完成EditText的编辑时捕获一个事件。

如何实现?


1
@PadmaKumar 最自然的方式是当 EditText 失去焦点时 - 那么用户肯定已经完成了,但是从经验来看,似乎并不是所有的 Android 小部件都能正确地抓住焦点(我在 Spinners 和 Buttons 上遇到了这些问题) - 所以其他小部件可能不会失去焦点... - AgentKnopf
3
为什么这样一个大平台让我们处理这种简单的功能?谷歌至少可以将这些主要功能添加到他们的支持库中,而不会失去任何东西。 - MBH
如果您知道用户输入的字符串的确切长度,则可以在afterTextChanged中获取文本。例如:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() } - Shailendra Madda
2
我正在想为什么在Android中这件事情如此困难,以至于我们(包括我自己)需要搜索并开始在线讨论,并提供许多不同的解决方案,每个解决方案都有10多行代码... - nsandersen
18个回答

200
更好的方式是,你还可以使用EditText的onFocusChange监听器来检查用户是否已经完成编辑:(不需要依赖于用户按下软键盘上的Done或Enter按钮)
 ((EditText)findViewById(R.id.youredittext)).setOnFocusChangeListener(new OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) { {
         // Validate youredittext
      }
    }
 });

注意:对于多个 EditText,您也可以让您的类实现 View.OnFocusChangeListener,然后将监听器设置为每个 EditText 并按如下方式验证它们。

((EditText)findViewById(R.id.edittext1)).setOnFocusChangeListener(this);
((EditText)findViewById(R.id.edittext2)).setOnFocusChangeListener(this);

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) {
        switch (view.getId()) {
           case R.id.edittext1:
                 // Validate EditText1
                 break;
           case R.id.edittext2:
                 // Validate EditText2
                 break;
        }
      }
    }

69
如果只有一个可聚焦的“EditText”,这种方法是无效的。它永远不会失去焦点。 - Bart Friederichs
如果我们只有一个EditText,最好的方法是什么?我必须使用onEditorAction吗? - Tux
3
如果只有一个EditText,则您需要对用户离开屏幕时进行的任何操作进行验证。 - Christine
我在这里创建了一个有关此问题的问题:https://dev59.com/8Jjga4cB1Zd3GeqPQeCg - ahitt6345
1
使用OnFocusChangeListener的一个注意点是:如果EditText获得焦点并且设备旋转,它将不会被调用!我通过在Activity的onPause()方法中(super.onPause()之前)放置someOtherView.requestFocus()来解决这个问题。这样就安全了,EditText失去焦点后监听器会被调用。 - allofmex
显示剩余4条评论

128

当用户编辑完毕后,他们将按下 DoneEnter

((EditText)findViewById(R.id.youredittext)).setOnEditorActionListener(
    new EditText.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH ||
                    actionId == EditorInfo.IME_ACTION_DONE ||
                    event != null &&
                    event.getAction() == KeyEvent.ACTION_DOWN &&
                    event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
                if (event == null || !event.isShiftPressed()) {
                   // the user is done typing. 

                   return true; // consume.
                }                
            }
            return false; // pass on to other listeners. 
        }
    }
);

56
你能确定这一点吗?我有一个习惯,就是会点开/进入下一个可编辑的字段,我几乎从不按Enter/Done键——而且根据我们客户的反馈来看,他们也是一样的。我在谈论一系列可编辑的文本框、组合框等。如果你只提供给用户一个输入字段,并强制他在可以前往其他字段之前必须按下按钮,那显然就是另一回事了…… - AgentKnopf
6
请记得为您的编辑文本添加 android:singleLine="true" 属性。感谢 https://dev59.com/pmUo5IYBdhLWcg3wnwj2。 - steven smith
1
好的解决方案,但可能会因为空指针而崩溃...在完成操作时,“event”可能为空,因此event.isShiftPressed()会导致崩溃。如果您使用此功能,请添加空指针检查。 - Georgie
添加了编辑以检查空的“事件”。 - Word Rearranger
我已经添加了EditorInfo.IME_ACTION_NEXT,这样就可以了吗? - Michał Ziobro
6
如果您误触关闭键盘的后退按钮,会怎么样? - philtz

70

我个人更喜欢在打完字后自动提交。以下是如何检测此事件的方法。

声明和初始化:

private Timer timer = new Timer();
private final long DELAY = 1000; // in ms

例如在onCreate()方法中的监听器

EditText editTextStop = (EditText) findViewById(R.id.editTextStopId);
    editTextStop.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }
        @Override
        public void onTextChanged(final CharSequence s, int start, int before,
                int count) {
            if(timer != null)
                timer.cancel();
        }
        @Override
        public void afterTextChanged(final Editable s) {
            //avoid triggering event when text is too short
            if (s.length() >= 3) {              

                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // TODO: do what you need here (refresh list)
                        // you will probably need to use
                        // runOnUiThread(Runnable action) for some specific
                        // actions
                        serviceConnector.getStopPoints(s.toString());
                    }

                }, DELAY);
            }
        }
    });

因此,当文本被更改时,计时器开始等待下一次更改发生。 当它们发生时,计时器被取消,然后再次启动。


12
这太棒了,但要注意计时器可能会导致内存泄漏,因此请改用Handler - Moh Mah
很好,谢谢。 - jojemapa
什么是serviceConnector? - Alireza Ghaffari
@altruistic 这只是我代码的一部分,我从中提取了答案。这是逻辑应该放置的地方。 - ZimaXXX
afterTextChanged 中使用 Handler(Looper.getMainLooper()) 调用 handler.postDelayed(runnable, DELAY_IN_MILLIS),并在 onTextChanged 中使用 handler.removeCallbacks(runnable) - Erkan

24

您可以使用setOnKeyListener或使用textWatcher来完成此操作:

设置文本观察器 editText.addTextChangedListener(textWatcher);

然后调用

private TextWatcher textWatcher = new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            //after text changed
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    };

32
恐怕这不是(完整的)解决方案——TextWatcher会在每次编辑后触发。如果您输入hello,它将会对h、e、l、l和o进行触发 - 它并不真正知道用户何时“完成”。如果TextWatcher真的知道小部件何时失去焦点并且用户移开了,那就太好了…… - AgentKnopf

7

如果您想在操作后隐藏键盘,则需要同时使用@Reno和@Vinayak B的答案。

textView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(textView.getWindowToken(), 0);
            return true;
        }
        return false;
    }
});

textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
             // your action here
        }
    }
});

7

我使用 Kotlin 解决了这个问题。

        var timer = Timer()
        var DELAY:Long = 2000

        editText.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable?) {
                Log.e("TAG","timer start")
                timer = Timer()
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        //do something
                    }
                }, DELAY)
            }

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

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                Log.e("TAG","timer cancel ")
                timer.cancel() //Terminates this timer,discarding any currently scheduled tasks.
                timer.purge() //Removes all cancelled tasks from this timer's task queue.
            }
        })

5
尽管许多答案都指向了正确的方向,但我认为它们都没有回答提问者所思考的问题。或者至少我对问题的理解有所不同,因为我在寻找类似问题的答案时得出了这样的结论:如何在用户没有按下按钮的情况下知道他停止输入,并触发某些操作(例如自动完成)?如果您想要实现这个目标,在onTextChanged中设置一个计时器,延迟一段时间(例如500-700毫秒)开始计时,来判断用户是否停止输入。每当输入新的字母时,请取消之前的计时器(或者至少使用某种标志,以确保当计时器到期时不执行任何操作)。以下是类似于我使用过的代码:
new Timer().schedule(new TimerTask() {
  @Override
  public void run() {
     if (!running) {                            
        new DoPost().execute(s.toString());
  });
 }
}, 700);

请注意,我在我的异步任务中修改了运行布尔标志(任务从服务器获取json以进行自动完成)。
还要记住,这会创建许多计时器任务(我认为它们被安排在同一个线程上,但必须检查这一点),因此可能有许多可以改进的地方,但这种方法也有效,底线是您应该使用定时器,因为没有“用户停止输入事件”。

请问您能否提供更多关于此的信息和示例代码? - John Ernest Guadalupe
代码的要点在答案中,如果需要更详细的示例,请参见此页面上ZimaXXX的答案,该答案使用相同的方法。 我不知道还能添加什么其他信息,如果您能指明哪些地方不清楚,我会尝试添加细节。 - Igor Čordaš

5

一种不同的方法...这里有一个示例: 如果用户在输入时有600-1000毫秒的延迟,您可能认为他已经停下了。

 myEditText.addTextChangedListener(new TextWatcher() {
             
            private String s;
            private long after;
   private Thread t;
            private Runnable runnable_EditTextWatcher = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        if ((System.currentTimeMillis() - after) > 600)
                        {
                            Log.d("Debug_EditTEXT_watcher", "(System.currentTimeMillis()-after)>600 ->  " + (System.currentTimeMillis() - after) + " > " + s);
                            // Do your stuff
                            t = null;
                            break;
                        }
                    }
                }
            };
            
            @Override
            public void onTextChanged(CharSequence ss, int start, int before, int count) {
                s = ss.toString();
            }
            
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            
            @Override
            public void afterTextChanged(Editable ss) {
                after = System.currentTimeMillis();
                if (t == null)
                {
                    t = new Thread(runnable_EditTextWatcher);
                      t.start();
                }
            }
        });


3

好的,这肯定可以100%正常工作。

首先,您需要设置监听器以检测键盘是显示还是隐藏。 如果键盘正在显示,则用户可能正在输入,否则已完成输入。

final View activityRootView = findViewById(android.R.id.content);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    activityRootView.getWindowVisibleDisplayFrame(r);

                    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...

                        isTyping = true;
                    } else {
//to make sure this will call only once when keyboard is hide.
                        if(isTyping){
                            isTyping = false;
                        }
                    }
            }
        });

2
多个文本编辑怎么办?而且 heightDiff > 100 这个东西我觉得很可怕。 - Bondax
没问题,它运行良好。看一下这个链接:https://dev59.com/WHI95IYBdhLWcg3wyRM0 - Krit

2

当我尝试在聊天应用中实现“正在输入”功能时,遇到了同样的问题。 请尝试按以下方式扩展EditText:

public class TypingEditText extends EditText implements TextWatcher {

private static final int TypingInterval = 2000;


public interface OnTypingChanged {
    public void onTyping(EditText view, boolean isTyping);
}
private OnTypingChanged t;
private Handler handler;
{
    handler = new Handler();
}
public TypingEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context) {
    super(context);
    this.addTextChangedListener(this);
}

public void setOnTypingChanged(OnTypingChanged t) {
    this.t = t;
}

@Override
public void afterTextChanged(Editable s) {
    if(t != null){
        t.onTyping(this, true);
        handler.removeCallbacks(notifier);
        handler.postDelayed(notifier, TypingInterval);
    }

}

private Runnable notifier = new Runnable() {

    @Override
    public void run() {
        if(t != null)
            t.onTyping(TypingEditText.this, false);
    }
};

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }


@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { }

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接