如何为多个EditText使用单个TextWatcher?

133

我在视图布局中有三个 EditText 组件。有没有一种方法可以为这三个 EditText 使用一个单独的 TextWatcher


https://github.com/henrychuangtw/AutoInsertEditText - HenryChuang
13个回答

199

我刚遇到了这个问题。我通过创建一个内部类实现TextWatcher并将一个View作为参数传递来解决它。然后,在方法实现中,只需根据视图进行切换以查看Editable来自哪个视图。

声明:

private class GenericTextWatcher implements TextWatcher{

    private View view;
    private GenericTextWatcher(View view) {
        this.view = view;
    }

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

    public void afterTextChanged(Editable editable) {
        String text = editable.toString();
        switch(view.getId()){
            case R.id.name:
                model.setName(text);
                break;
            case R.id.email:
                model.setEmail(text);
                break;
            case R.id.phone:
                model.setPhone(text);
                break;
        }
    }
}

用法:

name = (EditText) findViewById(R.id.name);
name.setText(model.getName());
name.addTextChangedListener(new GenericTextWatcher(name));

email = (EditText) findViewById(R.id.email);
email.setText(model.getEmail());
email.addTextChangedListener(new GenericTextWatcher(email));

phone = (EditText) findViewById(R.id.phone);
phone.setText(model.getPhone());
phone.addTextChangedListener(new GenericTextWatcher(phone));

1
请问在上面的代码中,'model'是什么意思,应该在哪里声明它? - YuDroid
42
这个答案不是“为多个EditText设置单个TextWatcher”,而是使用了一个TextWatcher类的3个实例。因此,3个独立的TextWatcher控制着3个EditText。 - Bobs
2
建议的解决方案不是为某些EditText添加一个TextWatcher。请查看此答案:https://dev59.com/I2855IYBdhLWcg3wik-D#13787221 - Bobs
2
运行得非常好,将其与包含表单元素的片段结合使用,以在更改方向时不丢失数据。 - Mathijs Segers
1
@breceivemail 说实话,“单个TextWatcher”并不一定意味着单个实例,也可能是单个类。 - Malcolm
显示剩余4条评论

49

如果你想要仅在文本变化后比较可编辑内容:

@Override
public void afterTextChanged(Editable editable) {
    if (editable == mEditText1.getEditableText()) {
        // DO STH
    } else if (editable == mEditText2.getEditableText()) {
        // DO STH
    }
}

11
只有使用 == 才能正确运行,使用 .equals() 会无效。 - Jarett Millard
2
你真是个天才!这只是比较指针,而不是 Editable 上实际存储的值!是的! - Burak Tamtürk
5
如果 mEditText1 和 mEditText2 的文本相同,会怎么样? - Tuss
@tuss 这就是为什么它们被按引用而不是按值进行比较的原因。 - Joakim
1
@Tomasz 这个怎么工作的?你实现了 TextWatcher 吗?如果是的话,你不需要重写三个方法吗? - leonheess

13

MultiTextWatcher实现

public class MultiTextWatcher {

    private TextWatcherWithInstance callback;

    public MultiTextWatcher setCallback(TextWatcherWithInstance callback) {
        this.callback = callback;
        return this;
    }

    public MultiTextWatcher registerEditText(final EditText editText) {
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                callback.beforeTextChanged(editText, s, start, count, after);
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                callback.onTextChanged(editText, s, start, before, count);
            }

            @Override
            public void afterTextChanged(Editable editable) {
                callback.afterTextChanged(editText, editable);
            }
        });

        return this;
    }

    interface TextWatcherWithInstance {
        void beforeTextChanged(EditText editText, CharSequence s, int start, int count, int after);

        void onTextChanged(EditText editText, CharSequence s, int start, int before, int count);

        void afterTextChanged(EditText editText, Editable editable);
    }
}

使用方法

    new MultiTextWatcher()
            .registerEditText(editText1)
            .registerEditText(editText2)
            .registerEditText(editText3)
            .setCallback(new TextWatcherWithInstance() {
                @Override
                public void beforeTextChanged(EditText editText, CharSequence s, int start, int count, int after) {
                    // TODO: Do some thing with editText
                }

                @Override
                public void onTextChanged(EditText editText, CharSequence s, int start, int before, int count) {
                    // TODO: Do some thing with editText
                }

                @Override
                public void afterTextChanged(EditText editText, Editable editable) {
                    // TODO: Do some thing with editText
                }
            });

你为什么要创建另一个类来链接它们?!我的意思是,你仍然不知道变化来自哪个“TextView”。 - Farid
不错的解决方案。做了一些修改,现在已经准备就绪了。 - other Tall guy

13

这段代码将起作用

TextWatcher watcher = new TextWatcher() {
  @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            //YOUR CODE
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            //YOUR CODE
        }

        @Override
        public void afterTextChanged(Editable s) {
          String outputedText = s.toString();

  mOutputText.setText(outputedText);

        }
    };

然后在oncreate中添加这个内容

  mInputText.addTextChangedListener(watcher);
        e2.addTextChangedListener(watcher);
        e3.addTextChangedListener(watcher);
        e4.addTextChangedListener(watcher);

2
那么mOutputText是从哪里来的? - Kimi Chiu

11

如果你想要使用onTextChanged,请参考下面提到的hashCode()进行比较 -

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    if(charSequence.hashCode() == first_edit_text.getText().hashCode()){
        // do other things 
    }

    if(charSequence.hashCode() == second_edit_text.getText().hashCode()){
       // do other things 
    }

}

或者

如果你想使用 afterTextChanged,请比较下面提到的 Editable -

@Override
public void afterTextChanged(Editable editable) {
    if (editable == first_edit_text.getEditableText()) {
        // do other things 
    } else if (editable == second_edit_text.getEditableText()) {
       // do other things 
    }
}

10
这是我为 Kotlin 提供的解决方案。您可以简单地使用引用相等性 (===) 来检查同一对象,它完美地发挥作用。
val mTextWatcher = object : TextWatcher {
        override fun afterTextChanged(et: Editable?) {

            when {
                et === et1.editableText -> {
                    Toast.makeText(this@MainActivity, "EditText 1", Toast.LENGTH_LONG).show()
                }
                et === et2.editableText -> {
                    Toast.makeText(this@MainActivity, "EditText 2", Toast.LENGTH_LONG).show()
                }

            }
        }

        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }
        override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }
    }
    et1.addTextChangedListener(mTextWatcher)
    et2.addTextChangedListener(mTextWatcher)

你太棒了!!!这是正确的方法,而且有效。谢谢。 - Ajay
很高兴为您服务,@Ajay。 - Shohan Ahmed Sijan

6

我知道这是一个旧问题,但有正确的决定。我将写下自己的解决方案,希望能帮到某些人。

模拟一个经典例子,我们有N个EditText,如果所有字段都填满,我们想要显示按钮。这个例子很有意义,尤其是如果进一步为每个字段使用验证器。

我根据这个问题制作了一个示例,但您可以进行任何设置

MultiEditText.class

public class MultiEditText extends AppCompatActivity{

EditText ed_1, ed_2, ed_3;
Button btn_ok;

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

    ed_1 = (EditText) findViewById(R.id.ed_1);
    ed_2 = (EditText) findViewById(R.id.ed_2);
    ed_3 = (EditText) findViewById(R.id.ed_3);
    btn_ok = (Button) findViewById(R.id.btn_ok);
    btn_ok.setEnabled(false);

    //if want more here can cycle interface List

     EditText[] edList = {ed_1, ed_2, ed_3};
     CustomTextWatcher textWatcher = new CustomTextWatcher(edList, btn_ok);
     for (EditText editText : edList) editText.addTextChangedListener(textWatcher);

    }
}

现在看起来非常简单。

CustomTextWatcher.class

自定义文本监听器类

public class CustomTextWatcher implements TextWatcher {

View v;
EditText[] edList;

public CustomTextWatcher(EditText[] edList, Button v) {
    this.v = v;
    this.edList = edList;
}

@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) {
    for (EditText editText : edList) {
        if (editText.getText().toString().trim().length() <= 0) {
            v.setEnabled(false);
            break;
        }
        else v.setEnabled(true);
    }
  }
}

我将添加一个布局,以便您不会浪费时间。

multi_edit_text.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">

<EditText
    android:id="@+id/ed_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<EditText
    android:id="@+id/ed_2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_1"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<EditText
    android:id="@+id/ed_3"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_2"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<Button
    android:id="@+id/btn_ok"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_3"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp"
    android:text="OK" />
</RelativeLayout>

6

让你的类继承自Activity并实现TextWatcher。

然后通过多态的魔力,你只需要订阅事件。

这不会告诉你TextEdit改变了什么,但是使用这个和Sky Kelsey的答案的组合,你可以很好地解决这个问题。

public YourActivity extends Activity implements TextWatcher {

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

        //Subscribe to the events
        EditText txt1 = (EditText) findViewById(R.id.txt1);
        txt1.addTextChangedListener(this);

        EditText txt2 = (EditText) findViewById(R.id.txt2);
        txt2.addTextChangedListener(this);
    }

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

            EditText txt1 = (EditText) findViewById(R.id.txt1);
            EditText txt2 = (EditText) findViewById(R.id.txt2);
            // You probably only want the text value from the EditText. But you get the idea. 
                doStuff(txt1,txt2);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.calc, menu);
        return true;
    }

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // TODO Auto-generated method stub
    }
}

3
TextWatcher watcher = new TextWatcher(){

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

然后:

editText1.addTextChangedListener(watcher);
editText2.addTextChangedListener(watcher);
editText3.addTextChangedListener(watcher);

2
(抱怨帖警告)他可能对所有控件都有非常相似的验证代码,不想复制粘贴3次 :) 我以前也遇到过这个问题,为什么他们可以在 onClickListener 上发送生成单击的控件,而不是像 TextWatcher 这样的东西...我能想到的唯一解决方法是创建3个 TextWatchers,调用相同的过程,但使用指向它们各自编辑控件的指针。 - Torp
1
@Torp,@bie:这个答案可能会有所帮助:https://dev59.com/I2855IYBdhLWcg3wik-D#4283532 不确定它是否完全解决了这里提出的问题,但是,正如所示,您可以让CustomTextWatcher自动调用另一个函数,该函数接收传递的Editable。 - Kevin Coppock

3
public class MainActivity extends AppCompatActivity{
    EditText value1, value2;

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

        //instantiate EditText controls
        value1 = (EditText)findViewById(R.id.txtValue1);
        value2 = (EditText)findViewById(R.id.txtValue2);

        //set up text changed listener
        value1.addTextChangedListener(new TextChange(value1));               
        value2.addTextChangedListener(new TextChange(value2));                       

        //inner class
        private class TextChange implements TextWatcher {

             View view;
             private TextChange (View v) {
                 view = v;
             }

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

             }


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

                 switch (view.getId()) {
                     case R.id.txtValue1:
                         //insert your TextChangedListener codes here
                         break;

                     case R.id.txtValue2:
                         //insert your TextChangedListener codes here
                         break;
                 }
             }   
         }
     }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接