Span เป็นออบเจ็กต์มาร์กอัปที่มีประสิทธิภาพซึ่งคุณใช้จัดรูปแบบข้อความในระดับ
อักขระหรือย่อหน้าได้ การแนบช่วงกับออบเจ็กต์ข้อความช่วยให้คุณเปลี่ยน
ข้อความได้หลายวิธี เช่น การเพิ่มสี การทำให้ข้อความคลิกได้
การปรับขนาดข้อความ และการวาดข้อความในลักษณะที่กำหนดเอง นอกจากนี้ ช่วงยังเปลี่ยนพร็อพเพอร์ตี้ TextPaint วาดบน Canvas และเปลี่ยนเลย์เอาต์ข้อความได้ด้วย
Android มี Span หลายประเภทที่ครอบคลุมรูปแบบการจัดรูปแบบข้อความทั่วไปที่หลากหลาย นอกจากนี้ คุณยังสร้างช่วงของคุณเองเพื่อใช้การจัดรูปแบบที่กำหนดเองได้ด้วย
สร้างและใช้ Span
หากต้องการสร้างช่วง คุณสามารถใช้คลาสใดคลาสหนึ่งที่แสดงในตารางต่อไปนี้ คลาสจะแตกต่างกันไปตามว่าข้อความเองเปลี่ยนแปลงได้หรือไม่ มาร์กอัปข้อความเปลี่ยนแปลงได้หรือไม่ และโครงสร้างข้อมูลพื้นฐานใดที่มีข้อมูลช่วง
| ชั้น | ข้อความที่เปลี่ยนแปลงได้ | มาร์กอัปที่เปลี่ยนแปลงได้ | โครงสร้างข้อมูล |
|---|---|---|---|
SpannedString |
ไม่ | ไม่ | อาร์เรย์เชิงเส้น |
SpannableString |
ไม่ | ใช่ | อาร์เรย์เชิงเส้น |
SpannableStringBuilder |
ใช่ | ใช่ | แผนผังช่วง |
คลาสทั้ง 3 ขยายอินเทอร์เฟซ Spanned
SpannableString และ SpannableStringBuilder ยังขยายอินเทอร์เฟซ Spannable ด้วย
วิธีตัดสินใจว่าจะใช้ตัวเลือกใดมีดังนี้
- หากไม่ได้แก้ไขข้อความหรือมาร์กอัปหลังจากสร้างแล้ว ให้ใช้
SpannedString - หากต้องการแนบช่วงจำนวนเล็กน้อยกับออบเจ็กต์ข้อความเดียวและข้อความนั้นเป็นแบบอ่านอย่างเดียว ให้ใช้
SpannableString - หากต้องการแก้ไขข้อความหลังจากสร้างแล้วและต้องการแนบช่วงกับข้อความ ให้ใช้
SpannableStringBuilder - หากต้องการแนบช่วงจำนวนมากกับออบเจ็กต์ข้อความ ไม่ว่าข้อความนั้นจะเป็นแบบอ่านอย่างเดียวหรือไม่ก็ตาม ให้ใช้
SpannableStringBuilder
หากต้องการใช้ช่วง ให้เรียกใช้ setSpan(Object _what_, int _start_, int _end_, int
_flags_)
ในออบเจ็กต์ Spannable พารามิเตอร์ what หมายถึงช่วงที่คุณ
ใช้กับข้อความ และพารามิเตอร์ start และ end ระบุส่วน
ของข้อความที่คุณใช้ช่วง
หากแทรกข้อความภายในขอบเขตของช่วง ช่วงจะขยายโดยอัตโนมัติเพื่อ
รวมข้อความที่แทรก เมื่อแทรกข้อความที่ขอบเขตของช่วง
กล่าวคือ ที่ดัชนีเริ่มต้นหรือสิ้นสุด พารามิเตอร์flags
จะกำหนดว่าช่วงจะขยายเพื่อรวมข้อความที่แทรกหรือไม่ ใช้แฟล็ก Spannable.SPAN_EXCLUSIVE_INCLUSIVE
เพื่อรวมข้อความที่แทรก และใช้ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
เพื่อยกเว้นข้อความที่แทรก
ตัวอย่างต่อไปนี้แสดงวิธีแนบ
ForegroundColorSpan กับสตริง
Kotlin
val spannable = SpannableStringBuilder("Text is spantastic!") spannable.setSpan( ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE )
Java
SpannableStringBuilder spannable = new SpannableStringBuilder("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE );
ForegroundColorSpan
เนื่องจากตั้งค่าช่วงโดยใช้ Spannable.SPAN_EXCLUSIVE_INCLUSIVE ช่วง
จึงขยายเพื่อรวมข้อความที่แทรกที่ขอบเขตของช่วง ดังที่แสดงใน
ตัวอย่างต่อไปนี้
Kotlin
val spannable = SpannableStringBuilder("Text is spantastic!") spannable.setSpan( ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE ) spannable.insert(12, "(& fon)")
Java
SpannableStringBuilder spannable = new SpannableStringBuilder("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, // start 12, // end Spannable.SPAN_EXCLUSIVE_INCLUSIVE ); spannable.insert(12, "(& fon)");
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
คุณแนบช่วงหลายช่วงกับข้อความเดียวกันได้ ตัวอย่างต่อไปนี้แสดงวิธี สร้างข้อความที่เป็นตัวหนาสีแดง
Kotlin
val spannable = SpannableString("Text is spantastic!") spannable.setSpan(ForegroundColorSpan(Color.RED), 8, 12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan( StyleSpan(Typeface.BOLD), 8, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE )
Java
SpannableString spannable = new SpannableString("Text is spantastic!"); spannable.setSpan( new ForegroundColorSpan(Color.RED), 8, 12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ); spannable.setSpan( new StyleSpan(Typeface.BOLD), 8, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
ForegroundColorSpan(Color.RED) และ
StyleSpan(BOLD)
ประเภทช่วงของ Android
Android มี Span มากกว่า 20 ประเภทในแพ็กเกจ android.text.style Android จัดหมวดหมู่ช่วงด้วย 2 วิธีหลักๆ ดังนี้
- ลักษณะที่ช่วงส่งผลต่อข้อความ: ช่วงอาจส่งผลต่อลักษณะที่ปรากฏของข้อความหรือเมตริกข้อความ
- ขอบเขตของช่วง: ช่วงบางช่วงใช้กับอักขระแต่ละตัวได้ ขณะที่ช่วงอื่นๆ ต้องใช้กับทั้งย่อหน้า
ส่วนต่อไปนี้จะอธิบายหมวดหมู่เหล่านี้โดยละเอียด
ช่วงที่มีผลต่อลักษณะที่ปรากฏของข้อความ
ช่วงบางช่วงที่ใช้ในระดับอักขระจะส่งผลต่อลักษณะที่ปรากฏของข้อความ เช่น
การเปลี่ยนสีข้อความหรือพื้นหลัง และการเพิ่มขีดเส้นใต้หรือขีดทับ ช่วงเหล่านี้ขยายคลาส CharacterStyle
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีใช้ UnderlineSpan เพื่อขีดเส้นใต้
ข้อความ
Kotlin
val string = SpannableString("Text with underline span") string.setSpan(UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
SpannableString string = new SpannableString("Text with underline span"); string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
UnderlineSpan
ช่วงที่มีผลต่อลักษณะที่ปรากฏของข้อความเท่านั้นจะทริกเกอร์การวาดข้อความใหม่โดยไม่ต้อง
ทริกเกอร์การคำนวณเลย์เอาต์ใหม่ ช่วงเหล่านี้จะใช้ UpdateAppearance และขยาย CharacterStyle
คลาสย่อย CharacterStyle จะกำหนดวิธีวาดข้อความโดยให้สิทธิ์เข้าถึงเพื่อ
อัปเดต TextPaint
ช่วงที่มีผลต่อเมตริกข้อความ
ช่วงอื่นๆ ที่ใช้ในระดับอักขระจะส่งผลต่อเมตริกข้อความ เช่น ความสูงของบรรทัดและขนาดข้อความ ช่วงเวลาเหล่านี้จะขยายเวลาของคลาส
MetricAffectingSpan
ตัวอย่างโค้ดต่อไปนี้สร้าง
RelativeSizeSpan ซึ่ง
เพิ่มขนาดข้อความขึ้น 50%
Kotlin
val string = SpannableString("Text with relative size span") string.setSpan(RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
SpannableString string = new SpannableString("Text with relative size span"); string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
RelativeSizeSpan
การใช้ช่วงที่มีผลต่อเมตริกข้อความจะทําให้ออบเจ็กต์ที่สังเกต วัดข้อความอีกครั้งเพื่อให้เลย์เอาต์และการแสดงผลถูกต้อง เช่น การเปลี่ยน ขนาดข้อความอาจทําให้คําปรากฏในบรรทัดต่างๆ การใช้ช่วงก่อนหน้าจะทําให้เกิดการวัดซ้ำ การคํานวณเลย์เอาต์ข้อความใหม่ และการวาดข้อความใหม่
ช่วงที่มีผลต่อเมตริกข้อความจะขยายMetricAffectingSpan คลาส ซึ่งเป็นคลาสแบบนามธรรมที่ช่วยให้คลาสย่อยกำหนดวิธีที่ช่วงมีผลต่อการวัดข้อความได้โดยการให้สิทธิ์เข้าถึงTextPaint เนื่องจาก MetricAffectingSpan ขยาย
CharacterStyle คลาสย่อยจึงส่งผลต่อลักษณะที่ปรากฏของข้อความที่ระดับอักขระ
ช่วงที่มีผลต่อย่อหน้า
นอกจากนี้ ช่วงยังส่งผลต่อข้อความในระดับย่อหน้าได้ด้วย เช่น การเปลี่ยน
การจัดแนวหรือขอบของบล็อกข้อความ ช่วงที่มีผลกับทั้งย่อหน้า
ใช้ ParagraphStyle หากต้องการ
ใช้ช่วงเหล่านี้ ให้แนบช่วงกับทั้งย่อหน้า โดยไม่รวมอักขระขึ้นบรรทัดใหม่
ตอนท้าย หากคุณพยายามใช้ช่วงย่อหน้ากับสิ่งอื่นที่ไม่ใช่
ทั้งย่อหน้า Android จะไม่ใช้ช่วงเลย
รูปที่ 8 แสดงวิธีที่ Android แยกย่อหน้าในข้อความ
\n)
ตัวอย่างโค้ดต่อไปนี้ใช้QuoteSpanกับย่อหน้า โปรดทราบว่า
หากคุณแนบช่วงกับตำแหน่งอื่นที่ไม่ใช่จุดเริ่มต้นหรือจุดสิ้นสุดของ
ย่อหน้า Android จะไม่ใช้รูปแบบเลย
Kotlin
spannable.setSpan(QuoteSpan(color), 8, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Java
spannable.setSpan(new QuoteSpan(color), 8, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
QuoteSpan
ใช้กับย่อหน้า
สร้างช่วงที่กำหนดเอง
หากต้องการฟังก์ชันการทำงานมากกว่าที่ระบุไว้ใน Android span ที่มีอยู่ คุณสามารถใช้ Custom Span ได้ เมื่อใช้ช่วงของคุณเอง ให้ตัดสินใจว่าช่วงจะส่งผลต่อข้อความที่ระดับอักขระหรือระดับย่อหน้า และจะส่งผลต่อเลย์เอาต์หรือลักษณะที่ปรากฏของข้อความหรือไม่ ซึ่งจะช่วยให้คุณ พิจารณาได้ว่าคลาสฐานใดที่ขยายได้และอินเทอร์เฟซใดที่อาจต้อง นำไปใช้ โปรดใช้ตารางต่อไปนี้เพื่ออ้างอิง
| สถานการณ์ | คลาสหรืออินเทอร์เฟซ |
|---|---|
| ช่วงจะส่งผลต่อข้อความที่ระดับอักขระ | CharacterStyle |
| ช่วงมีผลต่อลักษณะที่ข้อความปรากฏ | UpdateAppearance |
| ช่วงมีผลต่อเมตริกข้อความ | UpdateLayout |
| ช่วงจะส่งผลต่อข้อความในระดับย่อหน้า | ParagraphStyle |
เช่น หากต้องการใช้ช่วงที่กำหนดเองซึ่งแก้ไขขนาดและสีของข้อความ ให้ขยาย RelativeSizeSpan ผ่านการรับค่า RelativeSizeSpan
จะขยาย CharacterStyle และใช้ 2 Update อินเทอร์เฟซ เนื่องจากคลาสนี้มีฟังก์ชันเรียกกลับสำหรับ updateDrawState และ updateMeasureState อยู่แล้ว
คุณจึงลบล้างฟังก์ชันเรียกกลับเหล่านี้เพื่อใช้ลักษณะการทำงานที่กำหนดเองได้ โค้ดต่อไปนี้สร้าง Span ที่กำหนดเองซึ่งขยาย RelativeSizeSpan และลบล้างการเรียกกลับ updateDrawState เพื่อตั้งค่าสีของ TextPaint
Kotlin
class RelativeSizeColorSpan( size: Float, @ColorInt private val color: Int ) : RelativeSizeSpan(size) { override fun updateDrawState(textPaint: TextPaint) { super.updateDrawState(textPaint) textPaint.color = color } }
Java
public class RelativeSizeColorSpan extends RelativeSizeSpan { private int color; public RelativeSizeColorSpan(float spanSize, int spanColor) { super(spanSize); color = spanColor; } @Override public void updateDrawState(TextPaint textPaint) { super.updateDrawState(textPaint); textPaint.setColor(color); } }
ตัวอย่างนี้แสดงวิธีสร้างช่วงที่กำหนดเอง คุณสามารถสร้างเอฟเฟกต์เดียวกันได้โดยใช้ RelativeSizeSpan และ ForegroundColorSpan กับข้อความ
ทดสอบการใช้งานช่วง
อินเทอร์เฟซ Spanned ช่วยให้คุณทั้งตั้งค่าช่วงและดึงช่วงจากข้อความได้ เมื่อทดสอบ ให้ใช้การทดสอบ JUnit ของ Android เพื่อยืนยันว่าได้เพิ่มช่วงที่ถูกต้อง
ในตำแหน่งที่ถูกต้อง แอปตัวอย่างการจัดรูปแบบข้อความ
มีช่วงที่ใช้มาร์กอัปกับหัวข้อย่อยโดยการแนบ
BulletPointSpan ไปยังข้อความ ตัวอย่างโค้ดต่อไปนี้แสดงวิธีทดสอบ
ว่าเครื่องหมายหัวข้อย่อยปรากฏตามที่คาดไว้หรือไม่
Kotlin
@Test fun textWithBulletPoints() { val result = builder.markdownToSpans("Points\n* one\n+ two") // Check whether the markup tags are removed. assertEquals("Points\none\ntwo", result.toString()) // Get all the spans attached to the SpannedString. val spans = result.getSpans<Any>(0, result.length, Any::class.java) // Check whether the correct number of spans are created. assertEquals(2, spans.size.toLong()) // Check whether the spans are instances of BulletPointSpan. val bulletSpan1 = spans[0] as BulletPointSpan val bulletSpan2 = spans[1] as BulletPointSpan // Check whether the start and end indices are the expected ones. assertEquals(7, result.getSpanStart(bulletSpan1).toLong()) assertEquals(11, result.getSpanEnd(bulletSpan1).toLong()) assertEquals(11, result.getSpanStart(bulletSpan2).toLong()) assertEquals(14, result.getSpanEnd(bulletSpan2).toLong()) }
Java
@Test public void textWithBulletPoints() { SpannedString result = builder.markdownToSpans("Points\n* one\n+ two"); // Check whether the markup tags are removed. assertEquals("Points\none\ntwo", result.toString()); // Get all the spans attached to the SpannedString. Object[] spans = result.getSpans(0, result.length(), Object.class); // Check whether the correct number of spans are created. assertEquals(2, spans.length); // Check whether the spans are instances of BulletPointSpan. BulletPointSpan bulletSpan1 = (BulletPointSpan) spans[0]; BulletPointSpan bulletSpan2 = (BulletPointSpan) spans[1]; // Check whether the start and end indices are the expected ones. assertEquals(7, result.getSpanStart(bulletSpan1)); assertEquals(11, result.getSpanEnd(bulletSpan1)); assertEquals(11, result.getSpanStart(bulletSpan2)); assertEquals(14, result.getSpanEnd(bulletSpan2)); }
ดูตัวอย่างการทดสอบเพิ่มเติมได้ที่ MarkdownBuilderTest ใน GitHub
ทดสอบช่วงที่กำหนดเอง
เมื่อทดสอบช่วง ให้ตรวจสอบว่า TextPaint มีการแก้ไขตามที่คาดไว้ และองค์ประกอบที่ถูกต้องปรากฏใน Canvas ตัวอย่างเช่น ลองพิจารณาการใช้งานช่วงที่กำหนดเองซึ่งเพิ่มหัวข้อย่อยไว้หน้าข้อความบางส่วน หัวข้อย่อยมีขนาดและสีที่ระบุ และมีช่องว่าง
ระหว่างขอบซ้ายของพื้นที่ที่วาดได้กับหัวข้อย่อย
คุณทดสอบลักษณะการทำงานของคลาสนี้ได้โดยการใช้การทดสอบ AndroidJUnit และตรวจสอบสิ่งต่อไปนี้
- หากใช้ช่วงอย่างถูกต้อง จุดหัวข้อย่อยที่มีขนาดและสีที่ระบุจะปรากฏบน Canvas และมีช่องว่างที่เหมาะสมระหว่างขอบซ้ายกับจุดหัวข้อย่อย
- หากคุณไม่ใช้ช่วงเวลาดังกล่าว ลักษณะการทำงานที่กำหนดเองจะไม่ปรากฏ
คุณดูการใช้งานการทดสอบเหล่านี้ได้ในตัวอย่าง TextStyling ใน GitHub
คุณสามารถทดสอบการโต้ตอบของ Canvas ได้โดยการจำลอง Canvas ส่งออบเจ็กต์ที่จำลองไปยังเมธอด drawLeadingMargin()
และตรวจสอบว่ามีการเรียกเมธอดที่ถูกต้องด้วยพารามิเตอร์ที่ถูกต้อง
ดูตัวอย่างการทดสอบช่วงเพิ่มเติมได้ใน BulletPointSpanTest
แนวทางปฏิบัติแนะนำสำหรับการใช้ช่วง
การตั้งค่าข้อความใน TextView มีหลายวิธีที่ประหยัดหน่วยความจำ ขึ้นอยู่กับความต้องการของคุณ
แนบหรือแยกช่วงโดยไม่เปลี่ยนข้อความพื้นฐาน
TextView.setText()
มีการโอเวอร์โหลดหลายรายการที่จัดการช่วงแตกต่างกัน ตัวอย่างเช่น คุณสามารถ
ตั้งค่าออบเจ็กต์ข้อความ Spannable ด้วยโค้ดต่อไปนี้
Kotlin
textView.setText(spannableObject)
Java
textView.setText(spannableObject);
เมื่อเรียกใช้โอเวอร์โหลดของ setText() TextView จะสร้างสำเนาของ Spannable เป็น SpannedString และเก็บไว้ในหน่วยความจำเป็น CharSequence
ซึ่งหมายความว่าข้อความและช่วงจะเปลี่ยนแปลงไม่ได้ ดังนั้นเมื่อคุณต้องการอัปเดตข้อความหรือช่วง ให้สร้างออบเจ็กต์ Spannable ใหม่และเรียกใช้ setText() อีกครั้ง ซึ่งจะทริกเกอร์การวัดและการวาดเลย์เอาต์ใหม่ด้วย
หากต้องการระบุว่าช่วงต้องเปลี่ยนแปลงได้ ให้ใช้
setText(CharSequence text, TextView.BufferType
type)
แทน ดังที่แสดงในตัวอย่างต่อไปนี้
Kotlin
textView.setText(spannable, BufferType.SPANNABLE) val spannableText = textView.text as Spannable spannableText.setSpan( ForegroundColorSpan(color), 8, spannableText.length, SPAN_INCLUSIVE_INCLUSIVE )
Java
textView.setText(spannable, BufferType.SPANNABLE); Spannable spannableText = (Spannable) textView.getText(); spannableText.setSpan( new ForegroundColorSpan(color), 8, spannableText.getLength(), SPAN_INCLUSIVE_INCLUSIVE);
ในตัวอย่างนี้ พารามิเตอร์
BufferType.SPANNABLE
ทำให้ TextView สร้าง SpannableString และออบเจ็กต์
CharSequence ที่ TextView เก็บไว้จะมีมาร์กอัปที่เปลี่ยนแปลงได้และข้อความที่เปลี่ยนแปลงไม่ได้ หากต้องการอัปเดต Span ให้ดึงข้อความเป็น Spannable แล้ว
อัปเดต Span ตามที่ต้องการ
เมื่อแนบ แยก หรือเปลี่ยนตำแหน่งช่วง ข้อความ TextView จะอัปเดตโดยอัตโนมัติ
เพื่อให้สอดคล้องกับการเปลี่ยนแปลงในข้อความ หากคุณเปลี่ยนแอตทริบิวต์ภายใน
ของสแปนที่มีอยู่ ให้เรียกใช้ invalidate() เพื่อทำการเปลี่ยนแปลงที่เกี่ยวข้องกับลักษณะที่ปรากฏ หรือ
requestLayout() เพื่อทำการเปลี่ยนแปลงที่เกี่ยวข้องกับเมตริก
ตั้งค่าข้อความใน TextView หลายครั้ง
ในบางกรณี เช่น เมื่อใช้ RecyclerView.ViewHolder
คุณอาจต้องการนำ TextView กลับมาใช้ซ้ำและตั้งค่าข้อความหลายครั้ง โดยค่าเริ่มต้น ไม่ว่าคุณจะตั้งค่า BufferType หรือไม่ก็ตาม TextView จะสร้างสำเนาของออบเจ็กต์ CharSequence และเก็บไว้ในหน่วยความจำ ซึ่งจะทำให้TextViewการอัปเดตCharSequenceทั้งหมดเป็นไปโดยเจตนา คุณจะอัปเดตออบเจ็กต์TextViewต้นฉบับCharSequenceเพื่ออัปเดตข้อความไม่ได้ ซึ่งหมายความว่าทุกครั้งที่คุณตั้งค่าข้อความใหม่ TextView จะสร้างออบเจ็กต์ใหม่
หากต้องการควบคุมกระบวนการนี้มากขึ้นและหลีกเลี่ยงการสร้างออบเจ็กต์เพิ่มเติม
คุณสามารถใช้ Spannable.Factory ของคุณเองและลบล้าง newSpannable() ได้
คุณสามารถส่งและส่งคืน CharSequence ที่มีอยู่เป็น Spannable แทนการสร้างออบเจ็กต์ข้อความใหม่ได้ ดังที่แสดงในตัวอย่างต่อไปนี้
Kotlin
val spannableFactory = object : Spannable.Factory() { override fun newSpannable(source: CharSequence?): Spannable { return source as Spannable } }
Java
Spannable.Factory spannableFactory = new Spannable.Factory(){ @Override public Spannable newSpannable(CharSequence source) { return (Spannable) source; } };
คุณต้องใช้ textView.setText(spannableObject, BufferType.SPANNABLE) เมื่อ
ตั้งค่าข้อความ ไม่เช่นนั้น ระบบจะสร้างแหล่งข้อมูล CharSequence เป็นอินสแตนซ์ Spanned
และแคสต์ไปยัง Spannable ไม่ได้ ซึ่งจะทำให้ newSpannable() แสดง ClassCastException
หลังจากลบล้าง newSpannable() แล้ว ให้บอก TextView ให้ใช้ Factory ใหม่โดยทำดังนี้
Kotlin
textView.setSpannableFactory(spannableFactory)
Java
textView.setSpannableFactory(spannableFactory);
ตั้งค่าออบเจ็กต์ Spannable.Factory เพียงครั้งเดียวหลังจากได้รับข้อมูลอ้างอิงถึง TextView หากใช้ RecyclerView ให้ตั้งค่าออบเจ็กต์ Factory เมื่อคุณ
ขยายมุมมองเป็นครั้งแรก ซึ่งจะช่วยหลีกเลี่ยงการสร้างออบเจ็กต์เพิ่มเติมเมื่อ RecyclerView ผูกไอเทมใหม่กับ ViewHolder
เปลี่ยนแอตทริบิวต์ช่วงภายใน
หากต้องการเปลี่ยนเฉพาะแอตทริบิวต์ภายในของช่วงที่เปลี่ยนแปลงได้ เช่น
สีหัวข้อย่อยในช่วงหัวข้อย่อยที่กำหนดเอง คุณสามารถหลีกเลี่ยงค่าใช้จ่ายเพิ่มเติมจากการเรียกใช้
setText() หลายครั้งได้โดยเก็บการอ้างอิงไปยังช่วงขณะที่สร้าง
เมื่อต้องการแก้ไขช่วง คุณสามารถแก้ไขการอ้างอิงแล้วเรียกใช้
invalidate() หรือ requestLayout() ใน TextView ได้ โดยขึ้นอยู่กับประเภทของ
แอตทริบิวต์ที่คุณเปลี่ยนแปลง
ในตัวอย่างโค้ดต่อไปนี้ การใช้เครื่องหมายหัวข้อย่อยที่กำหนดเองมี สีเริ่มต้นเป็นสีแดงซึ่งจะเปลี่ยนเป็นสีเทาเมื่อแตะปุ่ม
Kotlin
class MainActivity : AppCompatActivity() { // Keeping the span as a field. val bulletSpan = BulletPointSpan(color = Color.RED) override fun onCreate(savedInstanceState: Bundle?) { ... val spannable = SpannableString("Text is spantastic") // Setting the span to the bulletSpan field. spannable.setSpan( bulletSpan, 0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) styledText.setText(spannable) button.setOnClickListener { // Change the color of the mutable span. bulletSpan.color = Color.GRAY // Color doesn't change until invalidate is called. styledText.invalidate() } } }
Java
public class MainActivity extends AppCompatActivity { private BulletPointSpan bulletSpan = new BulletPointSpan(Color.RED); @Override protected void onCreate(Bundle savedInstanceState) { ... SpannableString spannable = new SpannableString("Text is spantastic"); // Setting the span to the bulletSpan field. spannable.setSpan(bulletSpan, 0, 4, Spanned.SPAN_INCLUSIVE_INCLUSIVE); styledText.setText(spannable); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Change the color of the mutable span. bulletSpan.setColor(Color.GRAY); // Color doesn't change until invalidate is called. styledText.invalidate(); } }); } }
ใช้ฟังก์ชันส่วนขยาย Android KTX
นอกจากนี้ Android KTX ยังมีฟังก์ชันส่วนขยายที่ช่วยให้การทำงานกับช่วง ง่ายขึ้นด้วย ดูข้อมูลเพิ่มเติมได้ที่เอกสารประกอบสำหรับแพ็กเกจ androidx.core.text