관리 메뉴

나만을 위한 블로그

[Android] TextWatcher란? 본문

Android

[Android] TextWatcher란?

참깨빵위에참깨빵 2022. 1. 20. 00:26
728x90
반응형

앱을 만들다 보면 editText에 입력한 값을 실시간으로 관찰하면서 입력값에 따른 처리를 해야 할 때가 있다.

그 때 가볍게 써먹을 수 있는 편리한 TextWatcher란 인터페이스가 있다. 이름부터 뭘 하는 인터페이스인지 감이 온다. 텍스트를 지켜 보고 있는 인터페이스다.

일단 인터페이스기 때문에 구현하면 TextWatcher가 갖고 있는 모든 메서드를 재정의해야 하는데, TextWatcher에는 아래 3가지 메서드가 있다.

 

  • beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
  • onTextChanged(CharSequence charSequence, int i, int i1, int i2)
  • afterTextChanged(Editable editable)

 

이제 각 메서드들이 하는 역할과 매개변수들은 무엇인지 확인해 본다.

 

beforeTextChanged

 

이름으로 무슨 짓을 하는지 유추해 보면 텍스트가 바뀌기 전에 호출되는 메서드 같다. 이게 맞는지 디벨로퍼에서 확인해보자.

 

https://developer.android.com/reference/android/text/TextWatcher

 

TextWatcher  |  Android Developers

 

developer.android.com

이 메서드는 s(=charSequence) 안에서 처음에 시작하는 문자 수가 뒤에 길이가 있는 새 텍스트로 대체됨을 알리기 위해 호출된다. 이 콜백에서 s를 변경하려고 시도하는 것은 오류다

 

번역기의 한계로 무슨 뜻인지 모르겠다. 파파고가 나를 배신했어

바로 구글링해서 어떤 역할을 하는지 찾아봤다.

 

https://stackoverflow.com/questions/20278382/differences-between-textwatcher-s-ontextchanged-beforetextchanged-and-aftertex/20278548

 

Differences between TextWatcher 's onTextChanged, beforeTextChanged and afterTextChanged

In my Android project, I have had to add a TextChangedListener (TextWatcher) to an edit text view. And there are three parts to it: onTextChanged() beforeTextChanged() afterTextChanged() What a...

stackoverflow.com

 

스택오버플로우에 아주 좋은 예시 gif가 있어서 가져왔다. 첫 글자 입력 시에는 반응하지 않다가 두 번째 글자가 입력되자 처음에 입력했던 글자를 텍스트뷰에 보여주는 걸 볼 수 있다.

그리고 입력했던 문자열들을 하나씩 지울 때마다 지우기 전의 글자가 보인다.

저기서 start, count, after가 의미하는 건 아래와 같다.

 

  • start : 붉게 강조된 텍스트의 시작 인덱스
  • count : 붉게 강조된 텍스트의 길이
  • after : 녹색으로 강조된 텍스트의 길이

 

즉 문자열(charSequence)이 start 위치로부터 count 만큼의 길이(after)로 변경되려고 한다는 내용을 전달하는 메서드다.

여기서 i가 start, i1이 count, i2가 after란 걸 알 수 있다.

 

onTextChanged

 

이름부터 텍스트가 변경되는 그 순간에 호출되는 메서드같다. 디벨로퍼에선 아래와 같이 설명한다.

 

이 메서드는 s 안에서 처음 시작하는 문자 수가 이전의 길이가 있는 이전 텍스트를 대체했음을 알리기 위해 호출된다. 이 콜백에서 s를 변경하려고 시도하는 건 오류다

 

위의 gif를 다시 보면 onTextChanged()는 글자가 입력될 때마다 호출되서 count가 바뀌는 걸 볼 수 있다.

또한 실시간으로 텍스트가 입력되거나 삭제될 때 로직을 추가하려면 이 곳에 할 수도 있다는 걸 알 수 있다.

 

 

afterTextChanged

 

이름부터 글자가 바뀌고 나서, 즉 editText에 글자 입력이 완료되는 순간에 호출되는 메서드같다. 아래는 디벨로퍼의 설명이다.

 

이 메서드는 s 내의 어딘가에서 텍스트가 변경됐음을 알리기 위해 호출된다. 이 콜백에서 추가로 변경하는 건 합법적이지만 무한 루프에 안 빠지게 주의하라.(다른 afterTextChanged()가 이미 다른 변경을 수행했고 오프셋을 무효화했기 때문에 변경 위치는 알 수 없다) 그러나 여기서 알아야 할 건 onTextChanged()에서 Spannable.setSpan()을 써서 위치를 표시한 다음 여기서 해당 범위가 끝나는 지점을 확인할 수 있다

 

백날 이런 글 봐봤자 잘 모르겠다. 실제로 써보자.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="24dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private final String TAG = this.getClass().getSimpleName();

    private TextView textView;
    private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textview);
        editText = findViewById(R.id.edittext);

        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                Log.e(TAG, "beforeTextChanged() - charSequence : " + charSequence);
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                Log.e(TAG, "onTextChanged() - charSequence : " + charSequence);
            }

            @Override
            public void afterTextChanged(Editable editable) {
                Log.e(TAG, "afterTextChanged() - editable : " + editable.toString());
            }
        });
    }
}

 

editText에 TextWatcher를 붙이려면 addTextChangedListener()를 붙이고 여기서 new TextWatcher()를 통해 필수 구현 메서드 3개를 재정의한다.

문자가 입력될 때마다 어떤 일이 일어나는지 확인하기 위해 로그를 찍었는데 실행시켜서 로그캣을 키고 글자를 입력하면 아래처럼 로그가 찍힌다.

 

 

실제로 빌드해서 로그로 확인해보면 더 좋을 것이다.

이걸 보고 고민되는 것이 실시간 처리 로직을 어디에 둘 것인지다. onTextChanged()와 afterTextChanged() 중 어디에 두더라도 문제없이 작동할 것 같다. 스택오버플로우에 참고할 만한 답변이 있어 가져왔다.

 

https://stackoverflow.com/questions/476848/android-textwatcher-aftertextchanged-vs-textwatcher-ontextchanged

 

Android TextWatcher.afterTextChanged vs TextWatcher.onTextChanged

Under what circumstances should I use afterTextChanged instead of onTextChanged and vice versa?

stackoverflow.com

editText의 변경 사항을 듣는 중이라면 처음 2개 메서드를 쓸 필요가 전혀 없다. 3번째 메서드로 새로운 값을 받고 필요한 경우 새 텍스트를 수정하겠다. 그러나 값에 발생하는 정확한 변경 사항을 추적해야 하는 경우 처음 2개 메서드를 사용한다. 변경 사항을 듣고 텍스트를 수정해야 하는 경우에도 3번째 메서드로 수정하겠다
반응형
Comments