Chỉnh sửa android - kết thúc sự kiện gõ


121

Tôi muốn bắt một sự kiện khi người dùng hoàn thành chỉnh sửa EditText.

Nó được hoàn thiện bằng cách nào?


1
@PadmaKumar Cách tự nhiên nhất sẽ là nếu mất tiêu điểm trên EditText - thì người dùng chắc chắn đã hoàn thành, tuy nhiên từ kinh nghiệm có vẻ như không phải tất cả các widget Android đều lấy nét chính xác (tôi gặp vấn đề với Spinners và Nút) - vì vậy các vật dụng khác có thể không mất tập trung ...
AgentKnopf

3
Tại sao một nền tảng lớn như vậy cho phép chúng ta đối phó với loại chức năng đơn giản này? google sẽ không mất bất cứ thứ gì nếu họ thêm các tính năng chính như vậy vào thư viện hỗ trợ của họ ít nhất
MBH

Nếu bạn biết độ dài chính xác của chuỗi người dùng sẽ nhập sau đó, hãy lấy văn bản trong afterTextChanged. Vd:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda

2
Tôi đang gãi đầu tự hỏi tại sao điều này lại khó khăn đến vậy trong Android mà chúng tôi (bao gồm cả bản thân tôi) cần tìm kiếm và bắt đầu một cuộc thảo luận trực tuyến và nhiều giải pháp khác nhau với hơn 10 dòng mã ..?
nsandersen

Câu trả lời:


119

Khi người dùng chỉnh sửa xong, họ sẽ nhấn DonehoặcEnter

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

50
Bạn có thể chắc chắn về nó? Tôi có thói quen gõ vào / vào trường có thể chỉnh sửa tiếp theo - Tôi gần như không bao giờ nhấn Enter / Done - và những gì tôi đã thấy từ khách hàng của mình, họ cũng không ... Tôi đang nói về một danh sách Edittexts / Comboboxes, v.v. Nếu bạn chỉ cung cấp cho người dùng một trường Nhập liệu và buộc anh ta nhấn nút trước khi anh ta có thể chuyển sang các trường khác thì đó rõ ràng là một câu chuyện khác ...
AgentKnopf

5
Hãy nhớ rằng, bạn cũng phải cài đặt android: singleLine = "true" cho văn bản chỉnh sửa của mình. Cảm ơn stackoverflow.com/questions/15901863/ Lời
steven smith

1
Giải pháp tốt nhưng có thể sập với con trỏ null ... 'sự kiện' có thể là null trên hành động Xong nên event.isShiftPression () gặp sự cố. Nếu bạn sử dụng điều này thêm kiểm tra con trỏ null.
Georgie

Chỉnh sửa được thêm vào để kiểm tra null "Sự kiện".
Lời kể lại

3
Điều gì nếu bạn nhấn nút BACK đóng bàn phím?
user347187

183

Cách tốt hơn, bạn cũng có thể sử dụng trình nghe EditText onF FocusChange để kiểm tra xem người dùng đã chỉnh sửa xong chưa (Không cần phụ thuộc vào người dùng nhấn nút Xong hoặc Enter trên bàn phím Mềm)

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

Lưu ý: Đối với nhiều hơn một EditText, bạn cũng có thể để lớp của mình triển khai View.OnFocusChangeListenersau đó đặt các trình nghe cho từng bạn EditText và xác thực chúng như dưới đây

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

60
Điều này không hoạt động nếu chỉ có một tiêu điểm EditText. Nó sẽ không bao giờ mất tập trung.
Bart Friederichs

Cách tốt nhất nếu chúng ta chỉ có một EditText? Tôi có phải sử dụng onEditorActionkhông?
Thâp

3
Nếu chỉ có một EditText, bạn xác nhận bất cứ điều gì người dùng làm để rời khỏi màn hình.
Christine

Tôi đã thử điều này nhưng nó không hoạt động. Mỗi lần tôi nhấn enter, nó sẽ cho tôi một thông báo tương tự như thế này: W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=605.52246, y[0]=969.4336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=238238, downTime=235422, deviceId=0, source=0x1002 }Mặc dù tôi đã gõ một cái gì đó. Đây là một edittext chỉ chấp nhận số.
ahitt6345

1
Có một khía cạnh của việc sử dụng trình nghe OnF FocusChange: nó sẽ không được gọi nếu EditText có tiêu điểm và thiết bị được xoay! Tôi đã giải quyết nó bằng cách đưa someOtherView.requestF Focus () vào Activity onPause (). (trước super.onPause ()) Bằng cách đó, nó an toàn khi EditText mất tập trung và người nghe được gọi.
allofmex

60

Cá nhân tôi thích tự động gửi sau khi kết thúc gõ. Đây là cách bạn có thể phát hiện sự kiện này.

Tuyên bố và khởi tạo:

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

Trình nghe trong ví dụ 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);
            }
        }
    });

Vì vậy, khi văn bản được thay đổi, bộ đếm thời gian bắt đầu chờ đợi bất kỳ thay đổi tiếp theo nào xảy ra. Khi chúng xảy ra hẹn giờ bị hủy và sau đó bắt đầu lại.


11
điều này thật tuyệt vời, nhưng hãy cẩn thận rằng bộ hẹn giờ có thể gây rò rỉ bộ nhớ, vì vậy hãy sử dụng Handlerthay vào đó
Moh Mah

Thật tuyệt vời, tiếng Phạn.
VipPunkJoshers Dropopy

ServiceConnector là gì?
vị tha

@altruistic Đó chỉ là đoạn mã của tôi mà tôi đã đưa ra câu trả lời. Đó là nơi logic nên đi.
ZimaXXX

21

Bạn có thể làm điều đó bằng cách sử dụng setOnKeyListener hoặc sử dụng trình xem văn bản như:

Đặt trình xem văn bản editText.addTextChangedListener(textWatcher);

sau đó gọi

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) {

        }
    };

27
Tôi e rằng đây không phải là giải pháp (hoàn chỉnh) - textWatcher kích hoạt sau mỗi lần chỉnh sửa - nếu bạn gõ xin chào, nó sẽ kích hoạt cho h, e, l, l và o - nó thực sự không biết khi nào người dùng "hoàn thành" . Sẽ thật tuyệt nếu TextWatcher thực sự biết khi nào widget bị mất tiêu điểm và người dùng di chuyển vào ...
AgentKnopf

4

Mặc dù nhiều câu trả lời đi đúng hướng nhưng tôi nghĩ không ai trong số họ trả lời được tác giả của câu hỏi đang nghĩ gì. Hoặc ít nhất tôi đã hiểu câu hỏi khác đi vì tôi đang tìm câu trả lời cho vấn đề tương tự. Vấn đề là "Làm thế nào để biết khi nào người dùng dừng gõ mà không cần nhấn nút" và kích hoạt một số hành động (ví dụ: tự động hoàn thành). Nếu bạn muốn thực hiện việc này, hãy khởi động Bộ định thời trong onText Thay đổi độ trễ mà bạn sẽ xem xét người dùng đã dừng nhập (ví dụ 500-700ms), cho mỗi chữ cái mới khi bạn khởi động bộ định thời hủy bỏ cái trước đó (hoặc ít nhất là sử dụng một số loại gắn cờ rằng khi họ đánh dấu họ không làm gì cả). Đây là mã tương tự với những gì tôi đã sử dụng:

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

Lưu ý rằng tôi sửa đổi cờ boolean đang chạy bên trong tác vụ không đồng bộ của mình (Tác vụ lấy json từ máy chủ để tự động hoàn thành).

Ngoài ra, hãy nhớ rằng điều này tạo ra nhiều tác vụ hẹn giờ (tôi nghĩ rằng chúng được lên lịch trên cùng một Chủ đề nhưng sẽ phải kiểm tra điều này), vì vậy có thể có nhiều nơi để cải thiện nhưng cách tiếp cận này cũng hoạt động và điểm mấu chốt là bạn nên sử dụng Timer vì không có "Sự kiện người dùng dừng gõ"


Bạn có thể vui lòng cung cấp thêm thông tin và mã mẫu về điều này?
John Ernest Guadalupe

Ý chính của mã nằm trong câu trả lời, để biết ví dụ chi tiết hơn, vui lòng xem câu trả lời của ZimaXXX trên cùng trang này sử dụng cùng một cách tiếp cận. Tôi không biết những thông tin nào khác tôi có thể thêm, nếu bạn có thể chỉ định những gì không rõ ràng, tôi sẽ cố gắng thêm chi tiết.
Igor ordaš

4

cả @Reno và @Vinayak B trả lời cùng nhau nếu bạn muốn ẩn bàn phím sau khi hành động

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

3

Một cách tiếp cận khác ... đây là một ví dụ: Nếu người dùng có độ trễ 600-1000ms khi đang gõ, bạn có thể cân nhắc rằng mình đã dừng.

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


2

Được rồi, điều này sẽ làm việc 100% chắc chắn.

Trước tiên, bạn sẽ cần thiết lập trình nghe nếu bàn phím hiển thị hoặc ẩn. Nếu bàn phím hiển thị thì có lẽ người dùng đang gõ, nếu không thì gõ xong.

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

1
Điều gì về nhiều chỉnh sửa văn bản? Và những heightDiff > 100thứ đáng sợ imho.
Bondax

Không có vấn đề, nó hoạt động tốt. Hãy xem stackoverflow.com/questions/2150078/
Krit

2

Tôi đã giải quyết vấn đề này theo cách này. Tôi đã sử dụng 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.
            }
        })

0

Tôi gặp vấn đề tương tự và không muốn dựa vào người dùng nhấn Xong hoặc Enter.

Nỗ lực đầu tiên của tôi là sử dụng trình nghe onF FocusChange, nhưng theo mặc định, EditText của tôi đã lấy nét theo mặc định. Khi người dùng nhấn một số chế độ xem khác, onF FocusChange đã được kích hoạt mà không bao giờ người dùng đã gán cho nó tiêu điểm.

Giải pháp tiếp theo đã làm điều đó cho tôi, nơi gắn onF FocusChange nếu người dùng chạm vào EditText:

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

Trong trường hợp của tôi, khi người dùng chỉnh sửa xong, màn hình đã được đăng ký lại, do đó làm mới đối tượng myEditText. Nếu cùng một đối tượng được giữ, có lẽ bạn nên xóa trình nghe onF FocusChange trong onF FocusChange để ngăn sự cố onF FocusChange được mô tả ở đầu bài này.


Bạn đang thiết lập trình lắng nghe thay đổi tiêu điểm mỗi khi xảy ra sự kiện chạm, sẽ xảy ra vài lần một giây. Đừng làm điều này.
jsonfry

0

Tôi gặp vấn đề tương tự khi cố gắng thực hiện 'bây giờ gõ' trên ứng dụng trò chuyện. cố gắng mở rộng EditText như sau:

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

}


0

Tôi đã kết thúc cô ấy với cùng một vấn đề và tôi không thể sử dụng giải pháp với onEditorAction hoặc onF FocusChange và không muốn thử hẹn giờ. Một bộ đếm thời gian là quá nguy hiểm cho hương vị, bởi vì tất cả các luồng và quá khó đoán, vì bạn không biết khi nào mã của bạn được thực thi.

OnEditorAction không bắt được khi người dùng rời đi mà không sử dụng nút và nếu bạn sử dụng, vui lòng lưu ý rằng KeyEvent có thể là null. Trọng tâm không đáng tin cậy ở cả hai đầu, người dùng có thể lấy nét và rời khỏi mà không cần nhập bất kỳ văn bản nào hoặc chọn trường và người dùng không cần phải rời khỏi trường EditText cuối cùng.

Giải pháp của tôi sử dụng onF FocusChange và đặt cờ khi người dùng bắt đầu chỉnh sửa văn bản và chức năng để lấy văn bản từ chế độ xem tập trung cuối cùng mà tôi gọi khi cần.

Tôi chỉ xóa tiêu điểm trên tất cả các trường văn bản của mình để đánh lừa mã xem văn bản rời, Mã ClearF Focus chỉ được thực thi nếu trường có tiêu điểm. Tôi gọi hàm trong onSaveInstanceState để tôi không phải lưu cờ (mEditing) dưới dạng trạng thái của chế độ xem Chỉnh sửa và khi các nút quan trọng được nhấp và khi đóng hoạt động.

Hãy cẩn thận với TexWatcher vì đây là cuộc gọi thường xuyên. Tôi sử dụng điều kiện tập trung để không phản ứng khi mã onRestoreInstanceState nhập văn bản. Tôi

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

0

Tôi đã làm một cái gì đó giống như lớp trừu tượng này có thể được sử dụng thay cho kiểu 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
}

0

Đơn giản để kích hoạt kết thúc gõ trong EditText

làm việc cho tôi, nếu bạn sử dụng java chuyển đổi nó

Ở Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.