Span

ลองใช้วิธีการเขียน
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีใช้ข้อความในการเขียน

Span คือออบเจ็กต์มาร์กอัปที่มีประสิทธิภาพซึ่งคุณสามารถใช้เพื่อจัดรูปแบบข้อความที่ ระดับอักขระหรือย่อหน้า การรวม span กับวัตถุข้อความทำให้คุณสามารถ ข้อความได้หลายวิธี รวมถึงการเพิ่มสี การทำให้ข้อความคลิกได้ การปรับขนาดข้อความ และลากข้อความด้วยวิธีที่กำหนดเอง Span สามารถ เปลี่ยนคุณสมบัติของ TextPaint หรือวาดบน Canvas และเปลี่ยนเลย์เอาต์ข้อความ

Android มีระยะเวลาหลายประเภทซึ่งครอบคลุมข้อความทั่วไปมากมาย การจัดรูปแบบ คุณยังสามารถสร้างระยะเวลาของคุณเองเพื่อใช้การจัดรูปแบบที่กำหนดเองได้อีกด้วย

สร้างและใช้ระยะเวลา

ในการสร้างระยะเวลา คุณสามารถใช้ชั้นเรียนใดชั้นเรียนหนึ่งที่ระบุในตารางต่อไปนี้ คลาสจะแตกต่างกันไปขึ้นอยู่กับว่าตัวข้อความเปลี่ยนแปลงได้หรือไม่ หรือไม่ มาร์กอัปจะเปลี่ยนแปลงไม่ได้ และโครงสร้างข้อมูลที่สำคัญใดที่มีข้อมูล span

ชั้น ข้อความที่ปิดได้ มาร์กอัปที่ปิดได้ โครงสร้างข้อมูล
SpannedString ไม่ ไม่ อาร์เรย์เชิงเส้น
SpannableString ไม่ ใช่ อาร์เรย์เชิงเส้น
SpannableStringBuilder ใช่ ใช่ แผนผังช่วง

ทั้ง 3 ชั้นเรียนนี้จะขยายแพ็กเกจ Spanned ของ Google SpannableString และ SpannableStringBuilder ยังขยายระยะเวลา อินเทอร์เฟซของ Spannable

วิธีตัดสินใจเลือกใช้มีดังนี้

  • หากคุณไม่ได้แก้ไขข้อความหรือมาร์กอัปหลังจากการสร้าง ให้ใช้ SpannedString
  • ถ้าคุณต้องการแนบระยะเวลาจำนวนน้อยกับออบเจ็กต์ข้อความเดียว และ ข้อความนั้นเป็นแบบอ่านอย่างเดียว โปรดใช้ SpannableString
  • ถ้าคุณต้องการแก้ไขข้อความหลังจากการสร้าง และคุณต้องแนบ span กับ ข้อความ ให้ใช้ SpannableStringBuilder
  • หากคุณจำเป็นต้องแนบช่วงเวลาจำนวนมากกับออบเจ็กต์ข้อความ ไม่ว่า ไม่ว่าข้อความนั้นเป็นแบบอ่านอย่างเดียว ให้ใช้ SpannableStringBuilder

หากต้องการใช้ระยะเวลา โปรดโทรไปที่ setSpan(Object _what_, int _start_, int _end_, int _flags_) บนออบเจ็กต์ Spannable พารามิเตอร์ what หมายถึงระยะเวลาที่คุณอยู่ ที่ใช้กับข้อความ และพารามิเตอร์ start และ end จะแสดงส่วน ของข้อความที่คุณจะใช้สแปนด้วย

ถ้าคุณแทรกข้อความภายในขอบเขตของสแปน สแปนจะขยายเป็น รวมข้อความที่แทรกเข้ามา เมื่อแทรกข้อความที่ช่วงเวลา ขอบเขต ซึ่งก็คือดัชนีเริ่มต้นหรือสิ้นสุด ซึ่งก็คือธง จะกำหนดว่า span จะขยายเพื่อรวมข้อความที่แทรกหรือไม่ ใช้ เวลา 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
);
วันที่ รูปภาพแสดงข้อความสีเทาบางส่วนสีแดง
รูปที่ 1 ข้อความสไตล์ด้วย 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)");
วันที่ รูปภาพแสดงการรวมข้อความมากขึ้นเมื่อใช้ SPAN_EXCLUSIVE_INCLUSIVE
รูปที่ 2 ระยะเวลาจะขยายเพื่อรวม ข้อความเพิ่มเติมเมื่อใช้ 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)"
รูปที่ 3 ข้อความที่มีหลายช่วงเวลา ForegroundColorSpan(Color.RED) และ StyleSpan(BOLD)

ประเภทช่วง Android

Android มีสแปนกว่า 20 ประเภทใน android.text.style Android แบ่งประเภทระยะเวลาได้ 2 วิธีหลักๆ ดังนี้

  • ระยะเวลาส่งผลต่อข้อความอย่างไร: ระยะเวลาอาจส่งผลต่อลักษณะของข้อความหรือข้อความ เมตริกต่างๆ
  • ขอบเขตระดับ: บางช่วงสามารถใช้กับอักขระแต่ละตัวได้ ส่วนบางช่วงอาจใช้กับอักขระแต่ละตัวได้ ต้องใช้ได้กับทั้งย่อหน้า
รูปภาพแสดงหมวดหมู่ต่างๆ ของสแปน
รูปที่ 4 หมวดหมู่ของ Android Span

ส่วนต่อไปนี้จะอธิบายหมวดหมู่เหล่านี้โดยละเอียดยิ่งขึ้น

ระยะเวลาที่ส่งผลต่อลักษณะของข้อความ

ระยะเวลาบางรายการที่ใช้ในระดับอักขระจะส่งผลต่อลักษณะของข้อความ เช่น เปลี่ยนข้อความหรือสีพื้นหลัง และเพิ่มเส้นใต้หรือขีดทับ เหล่านี้ จะขยายรหัส 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);
วันที่ รูปภาพแสดงวิธีขีดเส้นใต้ข้อความโดยใช้ "ขีดเส้นใต้"
รูปที่ 5 ขีดเส้นใต้ข้อความโดยใช้ 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
รูปที่ 6 ข้อความมีขนาดใหญ่ขึ้นโดยใช้ RelativeSizeSpan

การใช้ช่วงที่ส่งผลกับเมตริกข้อความจะทำให้ออบเจ็กต์สังเกตการณ์ วัดข้อความอีกครั้งเพื่อให้ได้เลย์เอาต์และการแสดงภาพที่ถูกต้อง ตัวอย่างเช่น การเปลี่ยน ขนาดตัวอักษรอาจทำให้คำปรากฏในบรรทัดที่ต่างกัน ใช้ก่อนหน้า span จะทริกเกอร์การวัดค่าใหม่ การคํานวณเลย์เอาต์ข้อความอีกครั้ง และการวาดรูปแบบ ข้อความ

ระยะที่มีผลต่อเมตริกข้อความจะขยายคลาส MetricAffectingSpan ซึ่งเป็น คลาสนามธรรมที่ทำให้คลาสย่อยกำหนดได้ว่าสแปนจะส่งผลต่อการวัดข้อความอย่างไร ด้วยการให้สิทธิ์เข้าถึง TextPaint เนื่องจาก MetricAffectingSpan ขยายเวลา CharacterSpan คลาสย่อยจะส่งผลต่อลักษณะของข้อความที่อักขระ

ระยะเวลาที่ส่งผลต่อย่อหน้า

นอกจากนี้ระยะเวลายังสามารถส่งผลต่อข้อความในระดับย่อหน้า เช่น การเปลี่ยน การจัดแนวหรือระยะขอบของข้อความ ระยะเวลาที่ส่งผลต่อทั้งย่อหน้า ใช้งาน ParagraphStyle ถึง คุณใช้สแปนเหล่านี้ ให้คุณแนบสับกับทั้งย่อหน้า โดยไม่ใส่คำลงท้าย อักขระขึ้นบรรทัดใหม่ ถ้าคุณพยายามใช้ช่วงย่อหน้ากับสิ่งอื่นที่ไม่ใช่ เขียนทั้งย่อหน้า Android ไม่ใช้ Span เลย

รูปที่ 8 แสดงวิธีที่ Android แยกย่อหน้าในข้อความ

วันที่
รูปที่ 7 ใน Android ย่อหน้าจะลงท้ายด้วย อักขระขึ้นบรรทัดใหม่ (\n)

ตัวอย่างโค้ดต่อไปนี้ใช้ QuoteSpan ใน 1 ย่อหน้า โปรดทราบว่า หากคุณแนบช่วงกับตำแหน่งอื่นๆ ที่ไม่ใช่จุดเริ่มต้นหรือจุดสิ้นสุดของ ย่อหน้า 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);
วันที่ รูปภาพแสดงตัวอย่างของ เครื่องหมายคำพูดSpan
รูปที่ 8 QuoteSpan ใช้กับย่อหน้า

สร้างระยะเวลาที่กำหนดเอง

หากคุณต้องการฟังก์ชันมากกว่าที่มีใน Android รุ่นที่มีอยู่ คุณสามารถใช้ระยะเวลาที่กำหนดเองได้ เมื่อนำระยะเวลาของคุณเองไปใช้ ให้ตัดสินใจว่า ว่าช่วงของคุณมีผลต่อข้อความที่ระดับอักขระหรือระดับย่อหน้า และ จะส่งผลต่อเลย์เอาต์หรือลักษณะของข้อความหรือไม่ ซึ่งช่วย พิจารณาว่าคุณสามารถขยายคลาสพื้นฐานใดได้บ้างและอินเทอร์เฟซใดที่คุณอาจต้องใช้ เพื่อนำไปปฏิบัติ ใช้ตารางต่อไปนี้ในการอ้างอิง

สถานการณ์ คลาสหรืออินเทอร์เฟซ
ระยะเวลาจะมีผลต่อข้อความในระดับอักขระ CharacterStyle
ระยะเวลาจะมีผลต่อลักษณะของข้อความ UpdateAppearance
ระยะเวลามีผลต่อเมตริกข้อความ UpdateLayout
ระยะเวลาจะมีผลกับข้อความในระดับย่อหน้า ParagraphStyle

ตัวอย่างเช่น ถ้าคุณต้องการใช้ระยะเวลาที่กำหนดเองซึ่งจะปรับขนาดข้อความและ สี ขยาย RelativeSizeSpan ผ่านการรับค่า RelativeSizeSpan ขยาย CharacterStyle และใช้อินเทอร์เฟซ Update ทั้ง 2 รายการ เนื่องจาก มี Callback สำหรับ updateDrawState และ updateMeasureState อยู่แล้ว คุณสามารถลบล้าง Callback เหล่านี้เพื่อใช้ลักษณะการทำงานที่กำหนดเองของคุณได้ โค้ดต่อไปนี้สร้างช่วงที่กำหนดเองที่ขยาย RelativeSizeSpan และ ลบล้าง Callback 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 กับข้อความ

ทดสอบการใช้งาน Span

อินเทอร์เฟซ Spanned ให้คุณตั้งค่าระยะเวลา และยังเรียกระยะเวลาจาก ข้อความ เมื่อทำการทดสอบ ให้ใช้ Android JUnit ต่างๆ ทดสอบ เพื่อยืนยันว่าเพิ่ม span ที่ถูกต้องแล้ว ในตำแหน่งที่ถูกต้อง ตัวอย่างการจัดรูปแบบข้อความ แอป มี Span ที่ใช้มาร์กอัปกับสัญลักษณ์หัวข้อย่อยโดยการแนบ 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 กำลังตรวจสอบข้อมูลต่อไปนี้

  • หากคุณใช้ระยะเวลาอย่างถูกต้อง สัญลักษณ์หัวข้อย่อยของขนาดที่ระบุ และ สีจะปรากฏบนผืนผ้าใบ และมีพื้นที่ว่างที่เหมาะสมระหว่างด้านซ้าย และหัวข้อย่อย
  • หากคุณไม่ได้ใช้ระยะเวลานี้ ลักษณะการทำงานที่กำหนดเองจะไม่ปรากฏขึ้น

คุณสามารถดูการใช้งานการทดสอบเหล่านี้ได้ใน TextStyling ตัวอย่างใน GitHub

คุณสามารถทดสอบการโต้ตอบของ 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() แทนที่จะสร้างวัตถุข้อความใหม่ คุณสามารถแคสต์และส่งคืน 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();
            }
        });
    }
}

ใช้ฟังก์ชันส่วนขยาย KTX สำหรับ Android

Android KTX ยังมีฟังก์ชันส่วนขยายที่ช่วยให้ทำงานกับระยะเวลา ได้ง่ายยิ่งขึ้น โปรดดูข้อมูลเพิ่มเติมในเอกสารประกอบเกี่ยวกับ androidx.core.text ใหม่