Text Watcher를 트리거하지 않고 EditText 텍스트를 어떻게 변경할 수 있습니까?


104

EditTextCustomer Text Watcher 가있는 필드가 있습니다. 코드에서 내가 사용하는 EditText의 값을 변경해야합니다 .setText("whatever").

문제는 내가 변경하자마자 afterTextChanged무한 루프를 생성하는 메서드가 호출됩니다. afterTextChanged를 트리거하지 않고 텍스트를 어떻게 변경할 수 있습니까?

afterTextChanged 메서드에 텍스트가 필요하므로 TextWatcher.

답변:


70

감시자의 등록을 취소 한 다음 다시 등록 할 수 있습니다.

또는 사용자가 텍스트를 직접 변경 한시기를 감시자가 알 수 있도록 플래그를 설정할 수 있습니다 (따라서 무시해야 함).


당신의 힌트는 충분합니다. 실제로 onResume에 리스너를 등록했지만 onPause ()에 등록 취소하지 않았으므로 여러 번 호출했습니다.
Smeet

누군가 그것을 설명 할 수 있습니까? 기술적으로, 나는 안드로이드에 새로운, 더 자세한 pls가 필요합니다, 감사합니다.
Budi Mulyo 2011

116

짧은 답변

사용자 및 프로그램 트리거 이벤트를 구분하기 위해 현재 포커스가있는 뷰를 확인할 수 있습니다.

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

트윗 담아 가기 당신은 그게 필요하지 않습니다. 에서 hasFocus()직접 전화를 걸 수 있습니다 EditText.
Willi Mentzel

이상적이지 않습니다. edittext에 포커스가 있어도 트리거 리스너없이 setText ()를하려면 어떻게해야합니까?
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));

editable.replace () 대신 editText.setText () 를 사용하는 경우 텍스트를 빠르게 입력 할 때 약간의 지연을 느낄 수 있습니다 .


왜 이것이 반대표를 받았습니까? 이것은 내 문제에 대한 완벽한 해결책이지만 다른 것들은 작동하지 않았습니다. 그러나이 솔루션이 작동하기 위해 TextWatcher를 하위 클래스화할 필요는 없습니다. 감사합니다 Chan Chun Him.
Michael Fulton 2016

TextWatcher 등록 취소 및 재 등록에 대한 아이디어는 훌륭합니다. 그러나 et.setText ( "")는 작동하지만 s.replace (0, s.length (), "")는 작동하지 않습니다.
stevehs17 jul.

@ stevehs17, 나는 당신의 코드가 어떤지 정말로 모르겠지만 사용법이 훨씬 자세하게 업데이트되었습니다.
찬 천 그분

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

그렇기 때문에 멱 등성이 있어야한다고 말한 것입니다 (더 명확해야 함) ... 이상적이지는 않지만 작동하지 않는 단일 메서드 호출의 오버 헤드가 걱정된다면 실제로 하드 코어 최적화를해야합니다. 그렇다면 코드를 다시 구성하여 리스너를 호출하기 전에 제거하고 나중에 setText()다시 추가 할 수 있습니다.
Jeffrey Blattman

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

5

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 19-08-23

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

문제는 tagfiled를 사용하여 쉽게 해결할 수 있으며 editText의 초점을 다룰 필요도 없습니다.

프로그래밍 방식으로 텍스트 및 태그 설정

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

tagonTextChanged 확인

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

TextWatcher를 통해 EditText를 수정할 때 발생하는 순환 문제를 완화하는 추상 클래스를 만들었습니다.

/**
 * 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에 새 텍스트를 설정하는 것입니다.

또한 특정보기 대신 편집 가능 항목을 변경하면 감시자를 쉽게 재사용 할 수 있으며 일부 단위 테스트로 격리하여 테스트하여 원하는 결과를 얻을 수 있는지 확인할 수 있습니다.

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.