android EditText - yazma etkinliği tamamlandı


122

Kullanıcı EditText'i düzenlemeyi bitirdiğinde bir olay yakalamak istiyorum.

Nasıl yapılabilir?


1
@PadmaKumar En doğal yol, odak EditText'te kaybolursa olur - o zaman kullanıcı kesinlikle işini bitirir, ancak deneyimlerden tüm android widget'ları düzgün şekilde odaklanmıyor gibi görünüyor (Spinners ve Buttons ile ilgili sorunlarım vardı) - böylece diğer widget'lar odağı kaybetmeyebilir ...
AgentKnopf

3
Neden bu kadar büyük bir platform bu tür basit işlevlerle uğraşmamıza izin veriyor? Google, destek kitaplıklarına en azından bu tür ana özellikleri eklerse hiçbir şey kaybetmez
MBH

Kullanıcının gireceği dizenin tam uzunluğunu biliyorsanız, metni afterTextChanged olarak alın. Ör:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda

2
Bunun Android'de neden bu kadar zor olduğunu merak ediyorum (ben de dahil) bir çevrimiçi tartışma ve 10+ satır kodla birçok farklı çözümü aramamız ve başlatmamız gerekiyor ..?
nsandersen

Yanıtlar:


118

Kullanıcı düzenlemeyi bitirdiğinde, Doneveya tuşuna basacaktır.Enter

((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
Bundan emin misin? Bir sonraki düzenlenebilir alana dokunma alışkanlığım var - hemen hemen hiç Enter / Done tuşuna basmam - ve müşterilerimizden gördüklerim, onlar da yapmıyorlar ... Edittexts / Comboboxes vb. Listesinden bahsediyorum. Kullanıcıya yalnızca bir Giriş alanı verirseniz ve onu diğer alanlara ilerlemeden önce bir düğmeye basmaya zorlarsanız, bu açıkça farklı bir hikaye ...
AgentKnopf

5
Ayrıca, düzenleme metniniz için android: singleLine = "true" ayarına sahip olmanız gerektiğini unutmayın. Stackoverflow.com/questions/15901863/…
steven smith

1
İyi bir çözüm, ancak boş gösterici ile çökebilir ... 'olay', Bitti eyleminde boş olabilir, bu nedenle event.isShiftPressed () çöker. Bunu kullanırsanız, boş işaretçi kontrolleri ekleyin.
Georgie

Boş "Olay" olup olmadığını kontrol etmek için düzenleme eklendi.
Kelime Yeniden

3
klavyeyi kapatan GERİ düğmesine basarsanız ne olur?
user347187

183

Daha iyi bir yol, kullanıcının düzenleme yapıp yapmadığını kontrol etmek için EditText onFocusChange dinleyicisini de kullanabilirsiniz: ( Kullanıcının Dokunmatik klavyede Bitti veya Gir düğmesine basmasına güvenmeniz gerekmez )

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

Not: Birden fazla EditText için, sınıfınızın uygulamasına izin verebilir, View.OnFocusChangeListenerardından dinleyicileri her biriniz için EditText ayarlayabilir ve aşağıdaki gibi doğrulayabilirsiniz.

((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
Odaklanabilen tek bir şey varsa bu işe yaramaz EditText. Odağını asla kaybetmeyecek.
Bart Friederichs

Sadece bir tekimiz varsa en iyi yol nedir EditText? Kullanmak zorunda onEditorActionmıyım?
Tux

3
Yalnızca bir tane EditTextvarsa, kullanıcının ekrandan ayrılmak için ne yapacağını doğrularsınız.
Christine

Bunu denedim ama işe yaramadı. Enter tuşuna her 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 }bastığımda bana şuna benzer bir mesaj veriyordu: Bir şey yazmış olsam bile. Bu sadece sayıları kabul eden bir edittext.
ahitt6345

1
OnFocusChange dinleyicisini kullanmanın bir yan noktası vardır: EditText odağa sahipse ve cihaz döndürülürse çağrılmaz! Aktiviteler onPause () içine someOtherView.requestFocus () koyarak çözdüm. (super.onPause () 'dan önce) Bu şekilde EditText'in odağı kaybetmesi ve dinleyicinin çağrılması güvenlidir.
allofmex

60

Ben şahsen yazım bittikten sonra otomatik göndermeyi tercih ediyorum. İşte bu olayı nasıl tespit edebileceğiniz.

Bildirimler ve başlatma:

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

Örneğin onCreate () 'de dinleyici

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

Bu nedenle, metin değiştirildiğinde, zamanlayıcı sonraki değişikliklerin gerçekleşmesini beklemeye başlar. Oluştuklarında zamanlayıcı iptal edilir ve ardından tekrar başlatılır.


11
bu harika, ancak zamanlayıcıların bellek sızıntılarına neden olabileceğine dikkat edin, Handlerbunun yerine kullanın
Moh Mah

Harika, Thansk.
VipPunkJoshers Droopy

serviceConnector nedir?
özgecil

@altruistic Bu sadece cevabı çıkardığım kodumun bir parçası. Mantığın gitmesi gereken yer orası.
ZimaXXX

21

Bunu setOnKeyListener kullanarak veya aşağıdaki gibi bir textWatcher kullanarak yapabilirsiniz:

Metin izleyiciyi ayarla editText.addTextChangedListener(textWatcher);

sonra ara

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
Korkarım bu (eksiksiz) bir çözüm değil - textWatcher her düzenlemeden sonra ateşleniyor - merhaba yazarsanız, h, e, l, l ve o için ateşlenecek - kullanıcının ne zaman "bittiğini" gerçekten bilmiyor . TextWatcher gerçekten widget'ın odağı kaybettiğini ve kullanıcının devam ettiğini bilse harika olurdu ...
AgentKnopf

4

Pek çok yanıt doğru yönü işaret etse de hiçbirinin sorunun yazarının ne düşündüğünü yanıtladığını sanmıyorum. Ya da en azından soruyu farklı anladım çünkü benzer soruna cevap arıyordum. Sorun, "Kullanıcı bir düğmeye basmadan yazmayı bıraktığında nasıl anlaşılır" ve bazı eylemleri tetikler (örneğin, otomatik tamamlama). Bunu yapmak istiyorsanız, Timer'ı onTextChanged'da, kullanıcının yazmayı durdurduğunu düşüneceğiniz bir gecikmeyle (örneğin, 500-700ms) başlatın, zamanlayıcıyı başlattığınızda her yeni harf için öncekini iptal edin (veya en azından bir tür işaretlediklerinde hiçbir şey yapmadıklarını işaretleyin). İşte kullandığıma benzer kod:

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

Zaman uyumsuz görevimde çalışan boole bayrağını değiştirdiğimi unutmayın (Görev, json'u otomatik tamamlama için sunucudan alır).

Ayrıca, bunun birçok zamanlayıcı görevi oluşturduğunu da unutmayın (sanırım bunlar aynı İş Parçacığında planlanmışlardır ancak bunu kontrol etmeleri gerekir), bu nedenle muhtemelen iyileştirilecek birçok yer vardır, ancak bu yaklaşım da işe yarıyor ve sonuç olarak "Kullanıcı yazmayı durdurdu" olmadığı için bir Zamanlayıcı kullanın


Bu konuda daha fazla bilgi ve örnek kod verebilir misiniz?
John Ernest Guadalupe

Kodun özü cevapta, daha ayrıntılı örnek için lütfen aynı yaklaşımı kullanan bu aynı sayfadaki ZimaXXX cevabına bakınız. Başka hangi bilgileri ekleyebileceğimi bilmiyorum, neyin net olmadığını belirtirseniz ayrıntılar eklemeye çalışacağım.
Igor Čordaš

4

Eylemden sonra klavyeyi gizlemek istiyorsanız hem @ Reno hem de @ Vinayak B birlikte yanıt verir

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

Farklı bir yaklaşım ... işte bir örnek: Eğer kullanıcı yazarken 600-1000 ms gecikme yaşıyorsa, durduğunu düşünebilirsiniz.

 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

Tamam, bu kesinlikle% 100 çalışacak.

Öncelikle klavye gösteriliyorsa veya gizleniyorsa dinleyiciyi ayarlamanız gerekecek. Klavye gösteriliyorsa, muhtemelen kullanıcı yazıyordur, aksi halde yazmayı bitirmiştir.

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
peki ya birden çok metin düzenlemesi? Ve heightDiff > 100şeyler korkunç bir imho.
Bondax

Sorun değil, iyi çalışıyor. Şu stackoverflow.com/questions/2150078/… 'e
Krit

2

Bu sorunu bu şekilde çözdüm. Kotlin kullandım.

        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

Aynı sorunu yaşadım ve Done veya Enter tuşuna basan kullanıcıya güvenmek istemedim.

İlk girişimim onFocusChange dinleyicisini kullanmaktı, ancak varsayılan olarak odağı EditText'im aldı. Kullanıcı başka bir görünüme bastığında, kullanıcı ona odak atamadan onFocusChange tetiklendi.

Bir sonraki çözüm benim için yaptı, kullanıcı EditText'e dokunduysa onFocusChange eklendi:

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

Benim durumumda, kullanıcı düzenlemeyi bitirdiğinde ekran yeniden oluşturulmuş ve böylece myEditText nesnesi yenilenmiştir. Aynı nesne tutulursa, bu gönderinin başında açıklanan onFocusChange sorununu önlemek için muhtemelen onFocusChange dinleyicisini onFocusChange'den kaldırmanız gerekir.


Saniyede birkaç kez olacak bir dokunma olayı her gerçekleştiğinde bir odak değişikliği dinleyicisi ayarlıyorsunuz. Bunu yapma.
jsonfry

0

Sohbet uygulamasında 'şimdi yazarak' uygulamaya çalışırken aynı sorunu yaşadım. EditText'i aşağıdaki gibi genişletmeyi deneyin:

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

Onu aynı problemle bitirdim ve çözümü onEditorAction veya onFocusChange ile kullanamadım ve zamanlayıcıyı denemek istemedim. Bir zamanlayıcı, tüm iş parçacıkları nedeniyle may tadı için çok tehlikelidir ve kodun ne zaman yürütüldüğünü bilmediğiniz için çok öngörülemez.

Kullanıcı bir düğme kullanmadan ayrıldığında onEditorAction yakalanmaz ve eğer onu kullanırsanız, lütfen KeyEvent'in boş olabileceğini unutmayın. Odak her iki uçta da güvenilmezdir, kullanıcı herhangi bir metin girmeden veya alanı seçmeden odaklanabilir ve ayrılabilir ve kullanıcının son Metin Düzenleme alanını terk etmesi gerekmez.

Çözümüm, kullanıcı metni düzenlemeye başladığında onFocusChange ve bir bayrak seti ve gerektiğinde son odaklanmış görünümden metni almak için bir işlev kullanıyor.

Bırakılan metin görüntüleme kodunu kandırmak için tüm metin alanlarımdaki odağı temizlerim, clearFocus kodu yalnızca alanda odak varsa çalıştırılır. İşlevi onSaveInstanceState içinde çağırıyorum, böylece bayrağı (mEditing) EditText görünümünün bir durumu olarak ve önemli düğmeler tıklandığında ve etkinlik kapatıldığında kaydetmeme gerek kalmıyor.

TexWatcher'a dikkat edin çünkü sık sık çağrıdır, onRestoreInstanceState kodu metin girerken tepki vermemek için odak koşulunu kullanıyorum. ben

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

TextView.OnEditorActionListener türü yerine kullanılabilecek bu soyut sınıf gibi bir şey yaptım.

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

EditText'te yazmayı bitirmek için basit

benim için çalıştı, java kullanıyorsanız onu dönüştürün

Kotlin'de

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.