วิธีใช้ Single TextWatcher สำหรับ EditTexts หลายรายการ


คำตอบ:


190

ฉันเพิ่งเจอปัญหานี้ ฉันแก้ไขได้โดยการสร้างการใช้งานคลาสภายใน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

33
Single TextWatcher for multiple EditTextsคำตอบนี้ไม่ได้ เป็น 3 อินสแตนซ์ของคลาส TextWatcher เดียว TextWatchers ที่แยกกัน 3 ตัวจึงควบคุม 3 EditTexts
บ็อบ

2
โซลูชันที่แนะนำไม่ใช่ TextWatcher เดียวสำหรับ EditTexts บางรายการ ตรวจสอบคำตอบนี้: stackoverflow.com/a/13787221/779408
Bobs

2
ทำงานได้อย่างมีเสน่ห์โดยรวมสิ่งนี้เข้ากับส่วนที่มีองค์ประกอบของฟอร์มเพื่อไม่ให้ข้อมูลสูญหายเมื่อเปลี่ยนการวางแนว
Mathijs Segers

1
@breceivemail เพื่อความเป็นธรรม "TextWatcher เดียว" ไม่จำเป็นต้องหมายถึงอินสแตนซ์เดียวอาจเป็นคลาสเดียวก็ได้เช่นกัน
Malcolm

42

หากคุณต้องการใช้เฉพาะ afterTextChanged เปรียบเทียบการแก้ไข:

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

10
นี้จะทำงานได้อย่างถูกต้องเท่านั้นถ้าคุณใช้แทน== .equals()
Jarett Millard

2
คุณอัจฉริยะ! นี่เป็นเพียงการเปรียบเทียบพอยน์เตอร์ไม่ใช่ค่าจริงที่เก็บไว้ใน Editable! ใช่
Burak Tamtürk

3
จะเกิดอะไรขึ้นถ้า mEditText1 และ mEditText2 มีข้อความเดียวกัน
Tuss

@tuss นั่นคือเหตุผลที่พวกเขาถูกเปรียบเทียบโดยการอ้างอิงแทนที่จะเป็นมูลค่า
Joakim

1
@Tomasz วิธีนี้ทำงานอย่างไร? คุณใช้ TextWatcher หรือไม่ คุณไม่ต้องลบล้างสามวิธีถ้าคุณทำ?
leonheess

10

การใช้งาน 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

10

หากคุณต้องการใช้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 
    }
}

9

มันจะทำงานกับรหัสนี้

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

        }
    };

จากนั้นเพิ่มสิ่งนี้ในการสร้าง

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

แล้ว mOutputText มาจากไหน?
Kimi Chiu

5

ฉันรู้ว่านี่เป็นปัญหาเก่าและมีการตัดสินใจที่ถูกต้อง ฉันจะเขียนของพวกเขาเองบางทีมันอาจจะช่วยใครบางคนได้

การจำลองตัวอย่างคลาสสิกที่เรามี 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>

5

ให้ชั้นเรียนของคุณสืบทอดจากกิจกรรมและใช้ 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 ครั้ง :) ฉันเคยโดนมาก่อนทำไมพวกเขาถึงส่งการควบคุมที่ทำให้เกิดการคลิกบนClickListenerและไม่ ในสิ่งต่างๆเช่น TextWatcher ... วิธีแก้ปัญหาเดียวที่ฉันคิดได้คือสร้าง TextWatchers 3 ตัวที่เรียกขั้นตอนเดียวกัน แต่มีตัวชี้ไปยังตัวควบคุมการแก้ไขตามลำดับ
ทอ

1
@Torp, @bie: คำตอบนี้อาจเป็นที่สนใจ: stackoverflow.com/questions/4283062/…ไม่แน่ใจว่าจะแก้ปัญหาที่ระบุไว้ที่นี่ได้อย่างแน่นอน แต่ตามที่แสดงคุณสามารถให้ CustomTextWatcher เรียกใช้ฟังก์ชันอื่นโดยอัตโนมัติซึ่งใช้เวลาใน ผ่านแก้ไขได้
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;
                 }
             }   
         }
     }
}

3

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

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)

0

ฉันรู้ว่าคำถามนี้เก่า แต่ฉันต้องการแบ่งปันหนึ่งในวิธีแก้ปัญหาของฉัน (ใน Kotlin) ทางออกของฉันคือการปรับปรุงคำตอบของ @Shwarz Andrei เหตุผลของฉันคือถ้าคุณต้องการจัดการสิ่ง / วัตถุเพิ่มเติม

แทนที่จะส่งทั้งสองlist of EditTextsและa Buttonเป็นพารามิเตอร์คุณจะส่งผ่านlist of editTextไฟล์. จากนั้นภายในคลาสที่คุณกำหนดเองคุณจะใช้แลมด้าเช่น:

var hasFilled:((Boolean)->Unit)? = null 

จากนั้นคุณจะตั้งค่าหรือเพิ่มขึ้นภายในไฟล์ afterTextChanged

override fun afterTextChanged(p0: Editable?) {
       for (edit in _editTextList) {
           if (edit?.text.toString().trim().isEmpty()) {
                 hasFilled?.invoke(false) //<-- here 
               break
           } else {
               hasFilled?.invoke(true) //<--- here 
           }
       }
   }

ดังนั้นทุกครั้งจึงมีการเปลี่ยนแปลงใน EditText บางตัวแลมด้าของคุณถูกเรียกใช้

        val editTexts = listOf(emailEditText,passwordEditText) // your list of editText
        val textWatcher = customTextWatcher(editTexts) // initialize your custom object 
        editTexts.forEach { it -> it?.addTextChangedListener(textWatcher) } // each editText would listen for changes 


        textWatcher.hasFilled = { value ->  // now you have access to your lambda 
            if (value != true)  {
               // change the state of the button to unable 
              // do other things 
            } else {
              // change the state of the button to enable 
              // do other things 
            }
        }

0

นี่คือวิธีที่ฉันทำ:

สร้าง ArrayList ของ EditTexts จากนั้นใช้ for loop เพื่อใช้ TextWatcher สำหรับ EditTexts ทั้งหมดหากคุณมีพฤติกรรมเดียวสำหรับ EditTexts ทั้งหมดจากนั้นนำไปใช้ที่นั่นหากคุณมีพฤติกรรมเฉพาะสำหรับ EditTexts บางอย่างคุณสามารถใช้ if คำสั่งเพื่อเลือกและนำไปใช้กับข้อความแก้ไขแต่ละรายการ

นี่คือรหัสของฉัน:

ArrayList<EditText> editTexts = new ArrayList<>(); // Container list

editText1 = (EditText) findViewById(R.id.editText1);
editText2 = (EditText) findViewById(R.id.editText2);
editText3 = (EditText) findViewById(R.id.editText3);

editTexts.add(editText1); // editTexts[0]
editTexts.add(editText2); // editTexts[1]
editTexts.add(editText3); // editTexts[2]

for (final EditText editText : editTexts) { //need to be final for custom behaviors 
    editText.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) {
            //Apply general behavior for all editTexts

            if (editText == editTexts.get(1)) {
                //Apply custom behavior just for this editText                           
            }
        }
    });

}

หวังว่านี่จะช่วยได้


ขอบคุณสำหรับคำตอบ แต่นี่เป็นวิธีเดียวที่จะทำได้จริงหรือ ฉันหมายความว่ามันดูซับซ้อนสำหรับบางสิ่งบางอย่างเหมือนกันกับonTextChangedใน EditText การเพิ่มonFocusChangeวิดเจ็ตสำหรับหลาย ๆ วิดเจ็ตนั้นง่ายกว่ามากเนื่องจากส่งผ่านอ็อบเจ็กต์ผู้ส่งด้วยการเรียกใช้เมธอด จากนั้นคุณสามารถตรวจสอบว่าวัตถุใดที่ทำให้เกิดการโทรและจัดการจากที่นั่น
BdR
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.