Span เป็นออบเจ็กต์มาร์กอัปที่มีประสิทธิภาพซึ่งคุณใช้จัดรูปแบบข้อความในระดับ
อักขระหรือย่อหน้าได้ การแนบช่วงกับออบเจ็กต์ข้อความช่วยให้คุณเปลี่ยน
ข้อความได้หลายวิธี เช่น การเพิ่มสี การทำให้ข้อความคลิกได้
การปรับขนาดข้อความ และการวาดข้อความในลักษณะที่กำหนดเอง นอกจากนี้ Span ยังเปลี่ยนพร็อพเพอร์ตี้ของTextPaint
วาดบน Canvas
และเปลี่ยนเลย์เอาต์ข้อความได้ด้วย
Android มี 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 ที่มีอยู่ คุณสามารถใช้ Span ที่กำหนดเองได้ เมื่อใช้ช่วงของคุณเอง ให้ตัดสินใจว่าช่วงจะส่งผลต่อข้อความที่ระดับอักขระหรือระดับย่อหน้า และจะส่งผลต่อเลย์เอาต์หรือลักษณะของข้อความหรือไม่ ซึ่งจะช่วยให้คุณ พิจารณาได้ว่าคลาสฐานใดที่ขยายได้และอินเทอร์เฟซใดที่คุณอาจต้อง นำไปใช้ โปรดใช้ตารางต่อไปนี้เพื่ออ้างอิง
สถานการณ์ | คลาสหรืออินเทอร์เฟซ |
---|---|
ช่วงจะส่งผลต่อข้อความในระดับอักขระ | CharacterStyle |
ช่วงมีผลต่อลักษณะที่ข้อความปรากฏ | UpdateAppearance |
ช่วงของคุณส่งผลต่อเมตริกข้อความ | UpdateLayout |
ช่วงจะส่งผลต่อข้อความในระดับย่อหน้า | ParagraphStyle |
เช่น หากต้องการใช้ช่วงที่กำหนดเองซึ่งแก้ไขขนาดและสีของข้อความ ให้ขยาย RelativeSizeSpan
ผ่านการสืบทอด RelativeSizeSpan
ขยาย CharacterStyle
และใช้Update
อินเทอร์เฟซ 2 รายการ เนื่องจากคลาสนี้มีฟังก์ชันเรียกกลับสำหรับ updateDrawState
และ updateMeasureState
อยู่แล้ว
คุณจึงแทนที่ฟังก์ชันเรียกกลับเหล่านี้เพื่อใช้ลักษณะการทำงานที่กำหนดเองได้ โค้ดต่อไปนี้สร้างช่วงที่กำหนดเองซึ่งขยาย 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
เก็บไว้จะมีมาร์กอัปที่เปลี่ยนแปลงได้และ
ข้อความที่เปลี่ยนแปลงไม่ได้ หากต้องการอัปเดตช่วง ให้ดึงข้อความเป็น Spannable
แล้ว
อัปเดตช่วงตามที่ต้องการ
เมื่อแนบ แยก หรือเปลี่ยนตำแหน่งช่วง TextView
จะอัปเดตโดยอัตโนมัติ
เพื่อให้สอดคล้องกับการเปลี่ยนแปลงในข้อความ หากคุณเปลี่ยนแอตทริบิวต์ภายใน
ของช่วงที่มีอยู่ ให้เรียกใช้ invalidate()
เพื่อทำการเปลี่ยนแปลงที่เกี่ยวข้องกับลักษณะที่ปรากฏ หรือ
requestLayout()
เพื่อทำการเปลี่ยนแปลงที่เกี่ยวข้องกับเมตริก
ตั้งค่าข้อความใน TextView หลายครั้ง
ในบางกรณี เช่น เมื่อใช้
RecyclerView.ViewHolder
คุณอาจต้องการใช้ TextView
ซ้ำและตั้งค่าข้อความหลายครั้ง โดยค่าเริ่มต้น ไม่ว่าคุณจะตั้งค่า BufferType
หรือไม่ก็ตาม TextView
จะสร้างสำเนาของออบเจ็กต์ CharSequence
และเก็บไว้ในหน่วยความจำ การดำเนินการนี้จะทำให้TextView
การอัปเดตทั้งหมดเป็นไปโดยเจตนา คุณจะอัปเดตออบเจ็กต์ต้นฉบับCharSequence
เพื่ออัปเดตข้อความไม่ได้ ซึ่งหมายความว่าทุกครั้งที่คุณตั้งค่าข้อความใหม่ TextView
จะสร้างออบเจ็กต์ใหม่
หากต้องการควบคุมกระบวนการนี้มากขึ้นและหลีกเลี่ยงการสร้างออบเจ็กต์เพิ่มเติม
คุณสามารถใช้ Spannable.Factory
ของคุณเองและลบล้าง newSpannable()
ได้
คุณสามารถส่งและส่งคืนออบเจ็กต์ข้อความที่มีอยู่เป็น Spannable
แทนการสร้างออบเจ็กต์ข้อความใหม่ได้ ดังตัวอย่างต่อไปนี้CharSequence
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