ฉันจะเปลี่ยนข้อความ EditText โดยไม่เรียกใช้ Text Watcher ได้อย่างไร


104

ฉันมีEditTextช่องที่มี Customer Text Watcher อยู่ ในส่วนของรหัสฉันจำเป็นต้องเปลี่ยนค่าใน EditText .setText("whatever")ซึ่งเราใช้

ปัญหาคือทันทีที่ฉันทำการเปลี่ยนแปลงafterTextChangedวิธีการเรียกซึ่งสร้างลูปที่ไม่มีที่สิ้นสุด ฉันจะเปลี่ยนข้อความโดยไม่เรียกใช้ afterTextChanged ได้อย่างไร

ฉันต้องการข้อความในเมธอด afterTextChanged ดังนั้นไม่แนะนำให้ลบไฟล์TextWatcher.

คำตอบ:


70

คุณสามารถยกเลิกการลงทะเบียนผู้เฝ้าดูแล้วลงทะเบียนใหม่ได้

หรือคุณสามารถตั้งค่าสถานะเพื่อให้ผู้เฝ้าดูของคุณรู้ว่าคุณเพิ่งเปลี่ยนข้อความด้วยตัวเองเมื่อใด (ดังนั้นจึงควรเพิกเฉย)


คำใบ้ของคุณเพียงพอสำหรับฉัน อันที่จริงฉันกำลังลงทะเบียนผู้ฟังใน onResume แต่ไม่ได้ยกเลิกการลงทะเบียนใน onPause () ดังนั้นจึงมีการโทรหลายครั้ง
Smeet

ใครช่วยอธิบายได้ไหมในทางเทคนิคฉันเป็นมือใหม่ใน Android ต้องการรายละเอียดเพิ่มเติมกรุณาขอบคุณ
Budi Mulyo

117

คำตอบสั้น ๆ

คุณสามารถตรวจสอบว่ามุมมองใดในปัจจุบันมีจุดโฟกัสเพื่อแยกความแตกต่างระหว่างเหตุการณ์ที่เรียกใช้ของผู้ใช้และโปรแกรม

EditText myEditText = (EditText) findViewById(R.id.myEditText);

myEditText.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (myEditText.hasFocus()) {
            // is only executed if the EditText was directly changed by the user
        }
    }

    //...
});

คำตอบยาว

นอกเหนือจากคำตอบสั้น ๆ : ในกรณีที่myEditTextมีโฟกัสอยู่แล้วเมื่อคุณเปลี่ยนข้อความที่คุณควรโทรclearFocus()โดยใช้โปรแกรมจากนั้นคุณจะโทรหาsetText(...)และหลังจากที่คุณขอโฟกัสอีกครั้ง เป็นความคิดที่ดีที่จะใส่ไว้ในฟังก์ชันยูทิลิตี้:

void updateText(EditText editText, String text) {
    boolean focussed = editText.hasFocus();
    if (focussed) {
        editText.clearFocus();
    }
    editText.setText(text);
    if (focussed) {
        editText.requestFocus();
    }
}

สำหรับ Kotlin:

เนื่องจาก Kotlin สนับสนุนฟังก์ชันส่วนขยายฟังก์ชันยูทิลิตี้ของคุณอาจมีลักษณะดังนี้:

fun EditText.updateText(text: String) {
    val focussed = hasFocus()
    if (focussed) {
        clearFocus()
    }
    setText(text)
    if (focussed) {
        requestFocus()
    }
}

ในชิ้นส่วนgetActivity().getCurrentFocus()และ kotlinactivity?.currentFocus
Mohammad Reza Khahani

@MohammadRezaKhahani เฮ้! คุณไม่ต้องการสิ่งนั้น คุณสามารถโทรhasFocus()โดยตรงบนไฟล์EditText.
Willi Mentzel

ไม่เหมาะ จะเกิดอะไรขึ้นถ้าฉันต้องการ setText () โดยไม่ใช้ trigger listener แม้ว่า edittext จะมีโฟกัส
Jaya Prakash

31
public class MyTextWatcher implements TextWatcher {
    private EditText et;

    // Pass the EditText instance to TextWatcher by constructor
    public MyTextWatcher(EditText et) {
        this.et = et;
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Unregister self before update
        et.removeTextChangedListener(this);

        // The trick to update text smoothly.
        s.replace(0, s.length(), "text");

        // Re-register self after update
        et.addTextChangedListener(this);
    }
}

การใช้งาน:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

คุณอาจจะรู้สึกความล่าช้าเล็กน้อยเมื่อป้อนข้อความอย่างรวดเร็วถ้าคุณกำลังใช้editText.setText ()แทนeditable.replace ()


เหตุใดจึงถูกลดลง นี่เป็นวิธีแก้ปัญหาที่สมบูรณ์แบบสำหรับปัญหาของฉันในขณะที่ปัญหาอื่น ๆ ไม่ได้ผล ฉันจะเพิ่มว่าไม่จำเป็นต้องซับคลาส TextWatcher เพื่อให้โซลูชันนี้ทำงานได้ ขอบคุณชานชุนฮิม
Michael Fulton

ความคิดของคุณสำหรับการยกเลิกการลงทะเบียนและการลงทะเบียนใหม่ TextWatcher ใช้งานได้ดี อย่างไรก็ตามในขณะที่ et.setText ("") ทำงาน s.replace (0, s.length (), "") จะไม่
stevehs17

@ stevehs17 ฉันไม่รู้จริงๆว่ารหัสของคุณเป็นอย่างไร แต่การใช้งานได้รับการอัปเดตโดยละเอียดมากลองดู
Chan Chun Him

15

เคล็ดลับง่ายๆในการแก้ไข ... ตราบใดที่ตรรกะของคุณในการได้รับค่าข้อความแก้ไขใหม่นั้นเป็นสิ่งที่ไม่คาดคิด (ซึ่งอาจเป็นได้ แต่เพียงแค่พูด) ในวิธีการฟังของคุณแก้ไขเฉพาะข้อความแก้ไขหากค่าปัจจุบันแตกต่างจากครั้งล่าสุดที่คุณแก้ไขค่า

เช่น,

TextWatcher tw = new TextWatcher() {
  private String lastValue = "";

  @Override
  public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
  }

  @Override
  public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
  }

  @Override
  public void afterTextChanged(Editable editable) {

    // Return value of getNewValue() must only depend
    // on the input and not previous state
    String newValue = getNewValue(editText.getText().toString());
    if (!newValue.equals(lastValue)) {
      lastValue = newValue;

      editText.setText(newValue);
    }
  }
};

1
สิ่งนี้จะยังคงส่งผลให้เมธอดถูกเรียกสองครั้งอย่างไรก็ตามไม่?
Trevor Hart

ใช่นั่นคือเหตุผลที่ฉันบอกว่ามันควรจะเป็น idempotent (ควรทำให้ชัดเจนกว่านี้) ... ไม่เหมาะ แต่คุณต้องเพิ่มประสิทธิภาพแบบไม่ยอมใครง่ายๆหากคุณกังวลเกี่ยวกับค่าใช้จ่ายของการเรียกวิธีเดียวที่ไม่ได้ผล หากเป็นเช่นนั้นคุณสามารถจัดระเบียบรหัสใหม่เพื่อลบผู้ฟังก่อนที่จะเรียกsetText()และเพิ่มใหม่ในภายหลัง
Jeffrey Blattman

7

คุณสามารถใช้ไวยากรณ์ Kotlin DSL เพื่อมีโซลูชันทั่วไปสำหรับสิ่งนี้:

fun TextView.applyWithDisabledTextWatcher(textWatcher: TextWatcher, codeBlock: TextView.() -> Unit) {
    this.removeTextChangedListener(textWatcher)
    codeBlock()
    this.addTextChangedListener(textWatcher)
}

และภายใน TextWatcher ของคุณคุณสามารถใช้เป็น:

editText.applyWithDisabledTextWatcher(this) {
    text = formField.name
}

สะอาดกว่ามาก Kotlin DSL น่ากลัว
Anjal Saneen

นี่ไง. ขอบคุณ.
ACAkgul

6

ฉันใช้วิธีนั้น:

mEditText.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 (mEditText.isFocused()) { //<-- check if is focused 
                    mEditText.setTag(true);
                }
            }
        });

และทุกครั้งที่คุณต้องเปลี่ยนข้อความตามโปรแกรมให้ล้างโฟกัสก่อน

mEditText.clearFocus();
mEditText.setText(lastAddress.complement);

4

สิ่งนี้ใช้ได้ดีสำหรับฉัน

EditText inputFileName; // = (EditText)findViewbyId(R.id...)
inputFileName.addTextChangedListener(new TextWatcher() {
        public void afterTextChanged(Editable s) {

            //unregistering for event in order to prevent infinity loop
            inputFileName.removeTextChangedListener(this);

            //changing input's text
            String regex = "[^a-z0-9A-Z\\s_\\-]";
            String fileName = s.toString();
            fileName = fileName.replaceAll(regex, "");
            s.replace(0, s.length(), fileName); //here is setting new text

            Log.d("tag", "----> FINAL FILE NAME: " + fileName);

            //registering back for text changes
            inputFileName.addTextChangedListener(this);
        }

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

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

3

ปัญหาสามารถแก้ไขได้อย่างง่ายดายโดยใช้ไฟล์ที่tagยื่นและคุณไม่จำเป็นต้องจัดการกับโฟกัสของ editText

การตั้งค่าข้อความและแท็กโดยใช้โปรแกรม

editText.tag = "dummyTag"
editText.setText("whatever")
editText.tag = null

กำลังตรวจสอบtagใน onTextChanged

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    if (editText.tag == null) {
       // your code
    }
}

3

หากคุณต้องการจดจ่ออยู่กับEditTextข้อความการเปลี่ยนแปลงคุณสามารถขอโฟกัสได้:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}

1

ลองใช้ตรรกะนี้: ฉันต้องการ setText ("") โดยไม่ต้องวนซ้ำไม่สิ้นสุดและรหัสนี้ใช้ได้กับฉัน ฉันหวังว่าคุณจะสามารถปรับเปลี่ยนสิ่งนี้ให้เหมาะกับความต้องการของคุณ

        final EditText text= (EditText)findViewById(R.id.text);
        text.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(s.toString().isEmpty())return;
            text.setText("");
            //your code
        }
    });

1

นี่คือคลาสที่มีประโยชน์ซึ่งมีอินเทอร์เฟซที่ง่ายกว่า TextWatcher สำหรับกรณีปกติที่ต้องการเห็นการเปลี่ยนแปลงที่เกิดขึ้น นอกจากนี้ยังอนุญาตให้ละเว้นการเปลี่ยนแปลงถัดไปตามที่ OP ร้องขอ

public class EditTexts {
    public final static class EditTextChangeListener implements TextWatcher {
        private final Consumer<String> onEditTextChanged;
        private boolean ignoreNextChange = false;
        public EditTextChangeListener(Consumer<String> onEditTextChanged){
            this.onEditTextChanged = onEditTextChanged;
        }
        public void ignoreNextChange(){
            ignoreNextChange = true;
        }
        @Override public void beforeTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void onTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void afterTextChanged(Editable s) {
            if (ignoreNextChange){
                ignoreNextChange = false;
            } else {
                onEditTextChanged.accept(s.toString());
            }
        }
    }
}

ใช้แบบนี้:

EditTexts.EditTextChangeListener listener = new EditTexts.EditTextChangeListener(s -> doSomethingWithString(s));
editText.addTextChangedListener(listener);

เมื่อใดก็ตามที่คุณต้องการแก้ไขเนื้อหาeditTextโดยไม่ก่อให้เกิดการแก้ไขแบบวนซ้ำให้ทำดังนี้:

listener.ignoreNextChange();
editText.setText("whatever"); // this won't trigger the listener

0

ตัวแปรของฉัน:

public class CustomEditText extends AppCompatEditText{
    TextWatcher l;

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setOnTextChangeListener(TextWatcher l) {
        try {
            removeTextChangedListener(this.l);
        } catch (Throwable e) {}
        addTextChangedListener(l);
        this.l = l;
    }

    public void setNewText(CharSequence s) {
        final TextWatcher l = this.l;
        setOnTextChangeListener(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) {

            }
        });
        setText(s);
        post(new Runnable() {
            @Override
            public void run() {
                setOnTextChangeListener(l);
            }
        });
    }


}

ตั้งค่าผู้ฟังโดยใช้ setOnTextChangeListener () และตั้งค่าข้อความโดยใช้ setNewText เท่านั้น (ฉันต้องการแทนที่ setText () แต่ถือว่าเป็นที่สิ้นสุด)


0

ฉันได้สร้างคลาสนามธรรมซึ่งช่วยลดปัญหาที่เกิดขึ้นเป็นวงจรเมื่อมีการปรับเปลี่ยน EditText ผ่าน TextWatcher

/**
 * An extension of TextWatcher which stops further callbacks being called as a result of a change
 * happening within the callbacks themselves.
 */
public abstract class EditableTextWatcher implements TextWatcher {

    private boolean editing;

    @Override
    public final void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (editing)
            return;

        editing = true;
        try {
            beforeTextChange(s, start, count, after);
        } finally {
            editing = false;
        }
    }

    abstract void beforeTextChange(CharSequence s, int start, int count, int after);

    @Override
    public final void onTextChanged(CharSequence s, int start, int before, int count) {
    if (editing)
        return;

        editing = true;
        try {
            onTextChange(s, start, before, count);
        } finally {
            editing = false;
        }
    }

    abstract void onTextChange(CharSequence s, int start, int before, int count);

    @Override
    public final void afterTextChanged(Editable s) {
        if (editing)
            return;

        editing = true;
        try {
            afterTextChange(s);
        } finally {
            editing = false;
        }
    }    

    public boolean isEditing() {
        return editing;
    }

    abstract void afterTextChange(Editable s);
}

0

ง่ายมากตั้งค่าข้อความด้วยวิธีนี้

void updateText(EditText et, String text) {
   if (!et.getText().toString().equals(text))
       et.setText(text);
}

-2

คุณควรตรวจสอบให้แน่ใจว่าการดำเนินการเปลี่ยนแปลงข้อความของคุณมีความเสถียรและไม่เปลี่ยนแปลงข้อความหากไม่จำเป็นต้องเปลี่ยนแปลง โดยปกติจะเป็นเนื้อหาใด ๆ ที่ผ่านการเฝ้าดูมาแล้วครั้งหนึ่ง

ข้อผิดพลาดที่พบบ่อยที่สุดคือการตั้งค่าข้อความใหม่ใน EditText ที่เกี่ยวข้องหรือแก้ไขได้แม้ว่าข้อความนั้นจะไม่ได้เปลี่ยนแปลงก็ตาม

ยิ่งไปกว่านั้นหากคุณทำการเปลี่ยนแปลงแก้ไขแทนมุมมองเฉพาะบางส่วนคุณสามารถใช้ผู้เฝ้าดูของคุณซ้ำได้อย่างง่ายดายและคุณสามารถทดสอบแยกต่างหากด้วยการทดสอบหน่วยเพื่อให้แน่ใจว่าได้ผลลัพธ์ที่คุณต้องการ

เนื่องจาก Editable เป็นอินเทอร์เฟซคุณจึงสามารถใช้การใช้งานดัมมี่ที่พ่น RuntimeException ได้หากมีการเรียกวิธีการใด ๆ ที่พยายามเปลี่ยนเนื้อหาเมื่อทดสอบเนื้อหาที่ควรมีความเสถียร


-2

วิธีทำของฉัน:

ในส่วนการเขียน

        EditText e_q;

        e_q = (EditText) parentView.findViewWithTag("Bla" + i);

        int id=e_q.getId();
        e_q.setId(-1);
        e_q.setText("abcd...");
        e_q.setId(id);

ผู้ฟัง

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

        int id = view.getId();
        if(id==-1)return;

        ....

ใช้งานได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.