Programing

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

lottogame 2020. 9. 20. 10:30
반응형

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


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

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

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


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

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


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

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

myEditText.addTextChangedListener(new TextWatcher() {

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

    //...
});

편집 :LairdPleng이 (가) 경우 제대로 언급이 작동하지 않습니다 myEditText이미 포커스가 프로그래밍 방식으로 텍스트를 변경합니다. 따라서 전화하기 전에 Chack이 말한 대로 myEditText.setText(...)전화 해야이 문제도 해결됩니다.myEditText.clearFocus()


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

안녕하세요, EditText텍스트 변경에 집중해야한다면 포커스를 요청할 수 있습니다. 이것은 나를 위해 일했습니다.

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

나는 그렇게 사용합니다.

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

Kotlin DSL 구문을 사용하여 이에 대한 일반적인 솔루션을 얻을 수 있습니다.

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

그리고 TextWatcher 내에서 다음과 같이 사용할 수 있습니다.

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

이 논리를 시도해보십시오. 무한 루프로 이동하지 않고 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
        }
    });

다음은 변경 사항이 발생하는 것을 확인하려는 일반적인 경우에 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

이것은 나를 위해 잘 작동합니다.

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

The problem can be easily solved using tag filed and you don't even have to deal with editText's focus.

Setting the text and the tag programmatically

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

Checking for the tag in onTextChanged

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

My variant:

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


}

Set listeners only using setOnTextChangeListener() and set text only using setNewText (I wanted to override setText(), but it is final)


I've created an abstract class which mitigates the cyclic issue of when a modification to the EditText is made via a 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);
}

You should ensure your implementation of text changes is stable and does not change the text if no change is needed. Normally that would be any content that's already been through the watcher once.

The most common mistake is to set a new text in the associated EditText or the Editable even though the text was not actually changes.

On top of that, if you make your changes to the Editable instead of some specific View, you can easily resuse your watcher, and also you can test it in isolation with some unit tests to ensure it has the outcome you want.

Since Editable is an interface you could even use a dummy implementation of it that throws a RuntimeException if any of its methods are called that try to change its contents, when testing content that should be stable.

참고URL : https://stackoverflow.com/questions/9385081/how-can-i-change-the-edittext-text-without-triggering-the-text-watcher

반응형