ใช้ 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
- EditText.addTextChangedListener(…) ชื่อ method เป็น add ซึ่งนั่นหมายความ TextWatcher สามารถ add เข้าไปได้หลายตัว และสามารถสั่ง EditText.removeTextChangedListener(…) ได้ด้วย
- param ที่ส่งเข้ามาเป็น CharSequence กับ Editable ไม่มี String เลย เป็นไปได้อย่าเอา String เข้ามายุ่งเกี่ยวให้ใช้ StringBuilder จะดีกว่า
StringBuilder textBuilder = new StringBuilder(); เป็นต้น
NOTE: Editable เป็นแค่ interface นะ object จริงๆที่ส่งเข้ามาคือ SpannableStringBuilder
ปัญหา
จากโค้ดตัวอย่างใน Github
อธิบายการทำงานคร่าวๆของโค้ดด้านบนอาจดูซับซ้อนซักหน่อยแต่หลักๆคือ การดักตัวอักษรที่ 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 เป็นอันขาด ถ้าเข้าไปดูโค้ดข้างในกันเล่นๆ เราก๊อปมาให้ละ
>>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(…) มาก
ถ้าลองเข้าไปดูโค้ดกันเล่นๆ
เราคงไม่ได้ไล่ให้ดู แต่คร่าวๆมันคือการ replace ข้อความ และจัดการเกี่ยวกับข้อความทั้งหมด
คราวนี้การพิมพ์ของ User ก็จะลื่นไหลแล้วครับ หมดปัญหา user ไม่สามารถพิมพ์ตัวเลขได้เร็วๆได้และกดค้างเพื่อลบตัวเลข 😎
วันนี้คงไว้เท่านี้เจอกันบล็อกหน้าครับผม 😎
อย่าลืม 👏 ข้างล่าง และ share มนุษย์ Android คนอื่นด้วยหละ 😎