完成类型事件

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

怎样才能做到呢?

170253 次浏览

当用户完成编辑后,他/她将按 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.
}
}
);

你可以使用 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) {


}
};

更好的方法是,您还可以使用 EditText onFocusChange 监听器来检查用户是否完成了编辑: (无需依赖用户在软键盘上按“完成”或“输入”按钮)

 ((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;
}
}
}

我有同样的问题,不想依赖于用户按下完成或回车。

我的第一次尝试是使用 onFocusChange 侦听器,但是我的 EditText 在默认情况下获得了焦点。当用户按下其他视图时,onFocusChange 会在用户未分配焦点的情况下被触发。

下一个解决方案为我做到了这一点,如果用户触摸 EditText,onFocusChange 就会附加到该解决方案:

final myEditText = new EditText(myContext); //make final to refer in onTouch
myEditText.setOnTouchListener(new OnTouchListener() {


@Override
public boolean onTouch(View v, MotionEvent event) {
myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {


@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus){
// user is done editing
}
}
}
}
}

在我的示例中,当用户完成编辑时,屏幕会重新呈现,从而更新 myEditText 对象。如果保留相同的对象,您可能应该删除 onFocusChange 中的 onFocusChange 侦听器,以防止本文开头描述的 onFocusChange 问题。

虽然很多答案都指向正确的方向,但我认为没有一个答案能回答问题的作者在想什么。或者至少我对这个问题的理解是不同的,因为我在寻找类似问题的答案。 问题是“如何在用户不按按钮的情况下知道用户何时停止输入”并触发一些操作(例如自动完成)。如果你想这样做,启动 onTextChanged 中的计时器与一个延迟,你会认为用户停止输入(例如500-700ms) ,每一个新的字母,当你启动计时器取消较早的一个(或至少使用某种标志,当他们滴答他们不做任何事情)。下面是与我使用的代码类似的代码:

new Timer().schedule(new TimerTask() {
@Override
public void run() {
if (!running) {
new DoPost().execute(s.toString());
});
}
}, 700);

注意,我在异步任务中修改了正在运行的布尔标志(Task 从服务器获取 json 以实现自动完成)。

还要记住,这会创建许多定时器任务(我认为它们都安排在同一个线程上,但必须检查这一点) ,所以可能有很多地方需要改进,但这种方法也可以工作,底线是,你应该使用定时器,因为没有“用户停止输入事件”

如果你想在动作结束后隐藏键盘,@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
}
}
});

好吧,这肯定能百分之百奏效。

首先,如果键盘显示或隐藏,您将需要设置侦听器。 如果显示键盘,则可能用户正在输入,否则已完成输入。

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;
}
}
}
});

一种不同的方法... ... 这里有一个例子: 如果用户在输入时有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();
}
}
});

当我试图在聊天应用程序上实现“现在输入”时,也遇到了同样的问题。 尝试扩展 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) { }

}

我个人更喜欢在输入结束后自动提交。

声明和初始化:

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);
}
}
});

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

我用同样的问题结束了她,我不能使用 onEditorAction 或 onFocusChange 的解决方案,也不想尝试计时器。计时器太危险了,因为所有的线程和太不可预测,因为您不知道什么时候执行代码。

OnEditorAction 在用户不使用按钮离开时不捕获,如果您使用它,请注意 KeyEvent 可以为 null。焦点在两端都是不可靠的,用户可以在不输入任何文本或选择字段的情况下获得焦点并离开,而且用户不需要离开最后一个 EditText 字段。

我的解决方案使用 onFocusChange 和一个在用户开始编辑文本时设置的标志,以及一个从上一个聚焦视图中获取文本的函数,我在需要时调用该函数。

我只是清除了我的所有文本字段的焦点,以欺骗离开文本视图代码,clearFocus 代码只有在字段有焦点时才会执行。我在 onSaveInstanceState 中调用该函数,这样就不必将标志(mEditing)保存为 EditText 视图的状态,也不必在单击重要按钮和关闭活动时保存。

小心使用 TexWatcher,因为它经常被调用,当 onRestoreInstanceState 代码输入文本时,我使用聚焦条件不作出反应。 我

final EditText mEditTextView = (EditText) getView();


mEditTextView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {


}


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


}


@Override
public void afterTextChanged(Editable s) {
if (!mEditing && mEditTextView.hasFocus()) {
mEditing = true;
}
}
});
mEditTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {


@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && mEditing) {
mEditing = false;
///Do the thing
}
}
});
protected void saveLastOpenField(){
for (EditText view:getFields()){
view.clearFocus();
}
}

我已经完成了类似于这个抽象类的工作,它可以用来替代 TextView.OnEditorActionListener 类型。

abstract class OnTextEndEditingListener : TextView.OnEditorActionListener {


override fun onEditorAction(textView: TextView?, actionId: Int, event: KeyEvent?): Boolean {


if(actionId == EditorInfo.IME_ACTION_SEARCH ||
actionId == EditorInfo.IME_ACTION_DONE ||
actionId == EditorInfo.IME_ACTION_NEXT ||
event != null &&
event.action == KeyEvent.ACTION_DOWN &&
event.keyCode == KeyEvent.KEYCODE_ENTER) {


if(event == null || !event.isShiftPressed) {
// the user is done typing.
return onTextEndEditing(textView, actionId, event)
}
}
return false // pass on to other listeners
}


abstract fun onTextEndEditing(textView: TextView?, actionId: Int, event: KeyEvent?) : Boolean
}

在 EditText 中触发完成键入很简单

为我工作,如果你使用 Java 转换它

在 Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
Handler().postDelayed({
if (currentTextLength == searchTerm?.length) {
// your code
Log.d("aftertextchange", "ON FINISH TRIGGER")
}
}, 3000)
}

我用这种方法解决了这个问题,我用了 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.
}
})

用这门课

DelayedTextWatcher.java

import android.os.Handler;
import android.text.Editable;
import android.text.TextWatcher;
public class DelayedTextWatcher implements TextWatcher {


private String text;
private Runnable runnable = new Runnable() {
@Override
public void run() {
listener.onTimeout(text);
}
};


public static interface DelayedTextWatcherListener {
public void onTimeout(CharSequence text);
}


private long DELAY = 200;
private Handler handler = new Handler();
private DelayedTextWatcherListener listener;


public DelayedTextWatcher(DelayedTextWatcherListener l) {
listener = l;
}


@Override
public void afterTextChanged(Editable s) {
text = s.toString();
handler.postDelayed(runnable, DELAY);
}


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


@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
handler.removeCallbacks(runnable);
}


}

您可以使用 Handler来延迟。这个问题是由于多次调用过滤器和一个 count停止多次调用处理程序造成的。

final int[] Count = {0};
etSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(final CharSequence s, int start, int count,
int after) {
if (Count[0] == 0) {
Count[0]++;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
//apply filter call here
Count[0] = 0;
}
}, 1000);
}
}


@Override
public void onTextChanged(final CharSequence s, int start, int before,
int count) {
}


@Override
public void afterTextChanged(final Editable s) {


}
});

在 Kotlin,最简单的方法是:

fun TextView.addOnFinishedTypingListener(runnable: Runnable) {
val handler = Handler(Looper.getMainLooper())
addTextChangedListener(
{ _, _, _, _ -> },
{ _, _, _, _ -> handler.removeCallbacks(runnable) },
{ handler.postDelayed(runnable, 1000) }
)
setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || handler.hasCallbacks(runnable)) {
handler.removeCallbacks(runnable)
runnable.run()
}
}
false
}
}

你可以这样说:

binding.yourtextfield.addOnFinishedTypingListener {
//TODO your stuff
}

它在用户停止键入一秒钟或者按下“完成”后触发操作。要使后者工作,您还需要在 xml 中使用:

  android:imeOptions="actionDone"