ใช้ TextWatcher กับ EditText ให้ถูกวิธีเพื่อ UX ที่ดี

เนื่องจากเจ้าของบล็อกเคยได้โจทย์ให้ใส่ phone format ใหักับตัว EditText สิ่งแรกที่คิดขึ้นมาคงหนีไม่พ้น TextWatcher เป็นแน่แท้

แต่เราเจอปัญหาคือ user ไม่สามารถพิมพ์เบอร์เร็วๆได้ทำให้เกิด UX ที่แย่กับ user มาก และไม่สามารถกดค้างเพื่อลบตัวเลขแบบเร็วๆได้ด้วยซึ่งวันนี้เราจะมาดูสาเหตุและทำให้มันถูกต้องกัน

EditText.addTextChangedListener( 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 ){
...
}
} );

Tips

  1. EditText.addTextChangedListener(…) ชื่อ method เป็น add ซึ่งนั่นหมายความ TextWatcher สามารถ add เข้าไปได้หลายตัว และสามารถสั่ง EditText.removeTextChangedListener(…) ได้ด้วย
  2. param ที่ส่งเข้ามาเป็น CharSequence กับ Editable ไม่มี String เลย เป็นไปได้อย่าเอา String เข้ามายุ่งเกี่ยวให้ใช้ StringBuilder จะดีกว่า
StringBuilder textBuilder = new StringBuilder(); เป็นต้น

NOTE: Editable เป็นแค่ interface นะ object จริงๆที่ส่งเข้ามาคือ SpannableStringBuilder

ปัญหา

จากโค้ดตัวอย่างใน Github

DashTextWatcher-wrong

อธิบายการทำงานคร่าวๆของโค้ดด้านบนอาจดูซับซ้อนซักหน่อยแต่หลักๆคือ การดักตัวอักษรที่ user พิมพ์เข้ามาแล้วเอามาก map กับ ###-####-#### format นั่นเอง (สามารถไปดูละเอียดได้ที่ link Github) เช่น

09212345678 --เป็น--> 092-1234-5678แล้วเก็บไว้ในตัวแปร "strResult"

NOTE: สังเกตใน method TextWatcher.afterText(…) เราสั่ง EditText.removeTextChangedListener( this ) ตอนแรก และ EditText.addTextChangedListener( this ) ตอนสุดท้าย เพราะถ้าเราไม่ทำอย่างนี้ TextWatcher จะถูกเรียกขึ้นมาใหม่อีกถ้าเราสั่ง EditText.setText(…) มันจะทำให้ program เราติด infinity loop ไปเรื่อยๆนั่นเอง

ต่อมาให้สังเกตบรรทัดที่ 44

edt.setText( strResult );

method เจ้าปัญหาที่ไม่ควรใช้ใน TextWatcher เป็นอันขาด ถ้าเข้าไปดูโค้ดข้างในกันเล่นๆ เราก๊อปมาให้ละ

EditText.java → ref: http://grepcode.com/

>>What!!?<< นี่ EditText.setText(…) จริงๆใช่ไหมทำไมมันทำอะไรเยอะแยะขนาดนั้น

ใช่ครับมันเป็น method ที่หนักพอสมควรเลยเราขอไม่ไล่ให้ดูนะว่ามันทำอะไรบ้าง ซึ่งการที่นำมาใช้กับ TextWatcher จึงเป็นบาปมหันต์ เพราะทุกครั้งที่เรากดตัวอักษรที่คีย์บอร์ดหนึ่งตัวเพื่อใส่ลง EditText เจ้า TextWatcher จะทำงานและ setText(…) ใหม่ทุกครั้ง ซึ่งเป็นสามารถเหตุว่า user ไม่สามารถพิมพ์ตัวเลขได้เร็วๆรวมถึงการกดค้างเพื่อลบตัวเลขในทีเดียวด้วยเนื่องจาก event มันถูก cancel ไป 😫

เราจึงควรใช้ Editable ที่ส่งเป็น param เข้ามามากกว่า

แก้ไขให้ถูกต้อง

อยากบอกว่าแก้แค่บรรทัดเดียวคือบรรทัดที่ 44 (ที่เดิม)

edt.setText( strResult );เป็นs.append( strResult ); จบ 😱

ซึ่ง s ในที่นี้ก็คือ SpannableStringBuilder ที่ส่งเป็น param เข้ามา เราต้องใช้ object ตัวนี้จัดการข้อความใน EditText นะถึงจะถูก เพราะเป็น SpannableStringBuilder.append(…) เป็น method ที่เบากว่า EditText.setText(…) มาก

ถ้าลองเข้าไปดูโค้ดกันเล่นๆ

SpannableStringBuilder.java → ref: http://grepcode.com/

เราคงไม่ได้ไล่ให้ดู แต่คร่าวๆมันคือการ replace ข้อความ และจัดการเกี่ยวกับข้อความทั้งหมด

คราวนี้การพิมพ์ของ User ก็จะลื่นไหลแล้วครับ หมดปัญหา user ไม่สามารถพิมพ์ตัวเลขได้เร็วๆได้และกดค้างเพื่อลบตัวเลข 😎

วันนี้คงไว้เท่านี้เจอกันบล็อกหน้าครับผม 😎

เข้าไปติดตามกันได้ https://www.facebook.com/thekhaeng.io/

อย่าลืม 👏 ข้างล่าง และ share มนุษย์ Android คนอื่นด้วยหละ 😎

--

--

Nonthawit 👨🏻‍🚀 (น้ำแข็ง)
Nextzy

Tech CEO & Co-founder of The Existing Company┃Software Engineer┃Designer ┃Product Coach ┃Public Speaker ┃ Blogger┃Notion Expert