स्पैन

Compose को आज़माएं
Jetpack Compose, Android के लिए यूज़र इंटरफ़ेस (यूआई) का सुझाया गया टूलकिट है. Compose में टेक्स्ट का इस्तेमाल करने का तरीका जानें.

स्पैन, मार्कअप के ऐसे ऑब्जेक्ट होते हैं जिनका इस्तेमाल, वर्ण या पैराग्राफ़ के लेवल पर टेक्स्ट को स्टाइल करने के लिए किया जा सकता है. टेक्स्ट ऑब्जेक्ट में स्पैन जोड़कर, टेक्स्ट में कई तरह से बदलाव किया जा सकता है. जैसे, रंग जोड़ना, टेक्स्ट को क्लिक करने लायक बनाना, टेक्स्ट का साइज़ बढ़ाना या घटाना, और टेक्स्ट को अपनी पसंद के मुताबिक स्टाइल में दिखाना. स्पैन, TextPaint की प्रॉपर्टी में भी बदलाव कर सकते हैं. साथ ही, Canvas पर ड्रॉ कर सकते हैं और टेक्स्ट के लेआउट में बदलाव कर सकते हैं.

Android में कई तरह के स्पैन उपलब्ध हैं. इनकी मदद से, टेक्स्ट को स्टाइल करने के सामान्य पैटर्न में बदलाव किया जा सकता है. अपनी पसंद के मुताबिक स्टाइल लागू करने के लिए, स्पैन बनाए भी जा सकते हैं.

स्पैन बनाना और लागू करना

स्पैन बनाने के लिए, यहां दी गई टेबल में शामिल किसी भी क्लास का इस्तेमाल किया जा सकता है. क्लास इस आधार पर अलग-अलग होती हैं कि टेक्स्ट में बदलाव किया जा सकता है या नहीं, टेक्स्ट के मार्कअप में बदलाव किया जा सकता है या नहीं, और स्पैन डेटा में कौनसे डेटा स्ट्रक्चर का इस्तेमाल किया गया है.

क्लास बदलाव किया जा सकने वाला टेक्स्ट बदलाव किया जा सकने वाला मार्कअप डेटा स्ट्रक्चर
SpannedString नहीं नहीं लीनियर ऐरे
SpannableString नहीं हां लीनियर ऐरे
SpannableStringBuilder हां हां इंटरवल ट्री

ये तीनों क्लास, Spanned इंटरफ़ेस को एक्सटेंड करती हैं. SpannableString और SpannableStringBuilder भी Spannable इंटरफ़ेस को एक्सटेंड करती हैं.

यह तय करने का तरीका यहां दिया गया है कि इनमें से किसका इस्तेमाल करना है:

  • अगर आपको टेक्स्ट या मार्कअप में बदलाव नहीं करना है, तो SpannedString का इस्तेमाल करें.
  • अगर आपको किसी एक टेक्स्ट ऑब्जेक्ट में कुछ स्पैन जोड़ने हैं और टेक्स्ट सिर्फ़ पढ़ने के लिए है, तो SpannableString का इस्तेमाल करें.
  • अगर आपको टेक्स्ट बनाने के बाद उसमें बदलाव करना है और टेक्स्ट में स्पैन जोड़ने हैं, तो SpannableStringBuilder का इस्तेमाल करें.
  • अगर आपको किसी टेक्स्ट ऑब्जेक्ट में कई स्पैन जोड़ने हैं, तो SpannableStringBuilder का इस्तेमाल करें. भले ही, टेक्स्ट सिर्फ़ पढ़ने के लिए हो या नहीं.

स्पैन लागू करने के लिए, setSpan(Object _what_, int _start_, int _end_, int _flags_) ऑब्जेक्ट पर Spannable को कॉल करें. what पैरामीटर, उस स्पैन को दिखाता है जिसे टेक्स्ट पर लागू किया जा रहा है. वहीं, start और end पैरामीटर, टेक्स्ट के उस हिस्से को दिखाते हैं जिस पर स्पैन लागू किया जा रहा है.

अगर स्पैन की सीमाओं के अंदर टेक्स्ट डाला जाता है, तो स्पैन अपने-आप बढ़कर डाले गए टेक्स्ट को शामिल कर लेता है. स्पैन की सीमाओं पर टेक्स्ट डालने पर, flags पैरामीटर यह तय करता है कि स्पैन, डाले गए टेक्स्ट को शामिल करने के लिए बढ़ेगा या नहीं. स्पैन की सीमाएं, start या end इंडेक्स पर होती हैं. डाले गए टेक्स्ट को शामिल करने के लिए, 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 में android.text.style पैकेज में, स्पैन के 20 से ज़्यादा टाइप उपलब्ध हैं. Android, स्पैन को दो मुख्य तरीकों से कैटगरी में बांटता है:

  • स्पैन, टेक्स्ट पर कैसे असर डालता है: स्पैन, टेक्स्ट के दिखने के तरीके या टेक्स्ट की मेट्रिक पर असर डाल सकता है.
  • स्पैन का दायरा: कुछ स्पैन को अलग-अलग वर्णों पर लागू किया जा सकता है. वहीं, कुछ स्पैन को पूरे पैराग्राफ़ पर लागू करना ज़रूरी होता है.
अलग-अलग स्पैन कैटगरी दिखाने वाली इमेज
इमेज 4. Android में स्पैन की कैटगरी.

यहां दिए गए सेक्शन में, इन कैटगरी के बारे में ज़्यादा जानकारी दी गई है.

टेक्स्ट के दिखने के तरीके पर असर डालने वाले स्पैन

वर्ण के लेवल पर लागू होने वाले कुछ स्पैन, टेक्स्ट के दिखने के तरीके पर असर डालते हैं. जैसे, टेक्स्ट या बैकग्राउंड का रंग बदलना और अंडरलाइन या स्ट्राइकथ्रू जोड़ना. ये स्पैन, 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` का इस्तेमाल करके टेक्स्ट को अंडरलाइन करने का तरीका दिखाया गया है
इमेज 5. `UnderlineSpan` का इस्तेमाल करके अंडरलाइन किया गया टेक्स्ट.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. Text made larger using a RelativeSizeSpan.

टेक्स्ट की मेट्रिक पर असर डालने वाला स्पैन लागू करने पर, देखने वाला ऑब्जेक्ट, सही लेआउट और रेंडरिंग के लिए टेक्स्ट को फिर से मेज़र करता है. उदाहरण के लिए, टेक्स्ट का साइज़ बदलने पर, शब्द अलग-अलग लाइनों में दिख सकते हैं. ऊपर दिए गए स्पैन को लागू करने पर, टेक्स्ट के लेआउट को फिर से मेज़र किया जाता है, उसकी फिर से गिनती की जाती है, और टेक्स्ट को फिर से ड्रॉ किया जाता है.

टेक्स्ट की मेट्रिक पर असर डालने वाले स्पैन, MetricAffectingSpan क्लास को एक्सटेंड करते हैं. यह एक ऐब्स्ट्रैक्ट क्लास है. इसकी सब-क्लास, TextPaint का ऐक्सेस देकर यह तय करती हैं कि स्पैन, टेक्स्ट के मेज़रमेंट पर कैसे असर डालता है. MetricAffectingSpan, CharacterStyle को एक्सटेंड करता है. इसलिए, सब-क्लास, वर्ण के लेवल पर टेक्स्ट के दिखने के तरीके पर असर डालती हैं.

पैराग्राफ़ पर असर डालने वाले स्पैन

स्पैन, पैराग्राफ़ के लेवल पर भी टेक्स्ट पर असर डाल सकता है. जैसे, टेक्स्ट के ब्लॉक का अलाइनमेंट या मार्जिन बदलना. पूरे पैराग्राफ़ पर असर डालने वाले स्पैन लागू करते हैं ParagraphStyle. इन स्पैन का इस्तेमाल करने के लिए, इन्हें पूरे पैराग्राफ़ में जोड़ा जाता है. इसमें, नई लाइन के लिए इस्तेमाल होने वाला वर्ण शामिल नहीं होता. अगर पैराग्राफ़ स्पैन को पूरे पैराग्राफ़ के अलावा किसी और चीज़ पर लागू किया जाता है, तो Android उस स्पैन को लागू नहीं करता.

इमेज 8 में दिखाया गया है कि Android, टेक्स्ट में पैराग्राफ़ को कैसे अलग करता है.

इमेज 7. Android में, पैराग्राफ़ a नई लाइन (\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 का उदाहरण दिखाने वाली इमेज
इमेज 8. एक QuoteSpan पैराग्राफ़ पर लागू किया गया.

कस्टम स्पैन बनाना

अगर आपको Android में मौजूद स्पैन से ज़्यादा फ़ंक्शनैलिटी की ज़रूरत है, तो कस्टम स्पैन लागू किया जा सकता है. अपना स्पैन लागू करते समय, यह तय करें कि आपका स्पैन, वर्ण के लेवल पर टेक्स्ट पर असर डालता है या पैराग्राफ़ के लेवल पर. साथ ही, यह भी तय करें कि यह टेक्स्ट के लेआउट या दिखने के तरीके पर असर डालता है या नहीं. इससे आपको यह तय करने में मदद मिलती है कि किन बेस क्लास को एक्सटेंड किया जा सकता है और किन इंटरफ़ेस को लागू करना पड़ सकता है. जानकारी के लिए, यहां दी गई टेबल देखें:

परिदृश्य क्लास या इंटरफ़ेस
आपका स्पैन, वर्ण के लेवल पर टेक्स्ट पर असर डालता है. CharacterStyle
आपका स्पैन, टेक्स्ट के दिखने के तरीके पर असर डालता है. UpdateAppearance
आपका स्पैन, टेक्स्ट की मेट्रिक पर असर डालता है. UpdateLayout
आपका स्पैन, पैराग्राफ़ के लेवल पर टेक्स्ट पर असर डालता है. ParagraphStyle

उदाहरण के लिए, अगर आपको ऐसा कस्टम स्पैन लागू करना है जो टेक्स्ट के साइज़ और रंग में बदलाव करता है, तो RelativeSizeSpan को एक्सटेंड करें. इनहेरिटेंस की मदद से, RelativeSizeSpan, CharacterStyle को एक्सटेंड करता है और दो Update इंटरफ़ेस को लागू करता है. इस क्लास में, updateDrawState और updateMeasureState के लिए पहले से ही कॉलबैक मौजूद हैं. इसलिए, अपनी पसंद के मुताबिक व्यवहार लागू करने के लिए, इन कॉलबैक को ओवरराइड किया जा सकता है. यहां दिए गए कोड में, RelativeSizeSpan को एक्सटेंड करने वाला कस्टम स्पैन बनाया गया है. साथ ही, TextPaint का रंग सेट करने के लिए, updateDrawState कॉलबैक को ओवरराइड किया गया है:

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 इंटरफ़ेस की मदद से, स्पैन सेट किए जा सकते हैं. साथ ही, टेक्स्ट से स्पैन वापस भी पाए जा सकते हैं. जांच करते समय, Android JUnit टेस्ट लागू करें. इससे यह पुष्टि की जा सकती है कि सही स्पैन, सही जगहों पर जोड़े गए हैं . टेक्स्ट स्टाइलिंग के सैंपल ऐप्लिकेशन में, एक स्पैन मौजूद है. यह स्पैन, बुलेट पॉइंट पर मार्कअप लागू करता है. इसके लिए, 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 टेस्ट लागू करें. साथ ही, इन चीज़ों की जांच करें:

  • अगर स्पैन को सही तरीके से लागू किया जाता है, तो कैनवस पर तय साइज़ और रंग का बुलेट पॉइंट दिखता है. साथ ही, बाएं मार्जिन और बुलेट पॉइंट के बीच सही स्पेस मौजूद होता है.
  • अगर स्पैन लागू नहीं किया जाता है, तो कस्टम व्यवहार नहीं दिखता.

इन टेस्ट को लागू करने का तरीका, GitHub पर TextStyling के सैंपल में देखा जा सकता है.

कैनवस को मॉक करके, कैनवस के साथ इंटरैक्शन की जांच की जा सकती है. इसके लिए, मॉक किए गए ऑब्जेक्ट को 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 बनाता है. साथ ही, TextView में मौजूद CharSequence ऑब्जेक्ट में अब बदलाव किया जा सकने वाला मार्कअप और बदलाव न किया जा सकने वाला टेक्स्ट मौजूद है. स्पैन को अपडेट करने के लिए, टेक्स्ट को 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);

TextView का रेफ़रंस मिलने के तुरंत बाद, Spannable.Factory ऑब्जेक्ट को एक बार सेट करें. अगर RecyclerView का इस्तेमाल किया जा रहा है, तो व्यू को पहली बार इन्फ़्लेट करते समय, Factory ऑब्जेक्ट सेट करें. इससे, RecyclerView के ViewHolder में नया आइटम बाइंड करने पर, अतिरिक्त ऑब्जेक्ट बनाने से बचा जा सकता है.

स्पैन के इंटरनल एट्रिब्यूट में बदलाव करना

अगर आपको बदलाव किए जा सकने वाले स्पैन के सिर्फ़ किसी इंटरनल एट्रिब्यूट में बदलाव करना है, तो स्पैन का रेफ़रंस बनाए रखें. इससे, setText() को एक से ज़्यादा बार कॉल करने से होने वाले ओवरहेड से बचा जा सकता है. उदाहरण के लिए, कस्टम बुलेट स्पैन में बुलेट का रंग बदलना. स्पैन में बदलाव करने के लिए, रेफ़रंस में बदलाव करें. इसके बाद, बदले गए एट्रिब्यूट के टाइप के आधार पर, TextView पर invalidate() या requestLayout() को कॉल करें.

यहां दिए गए कोड के उदाहरण में, कस्टम बुलेट पॉइंट लागू करने पर, डिफ़ॉल्ट रंग लाल होता है. बटन पर टैप करने पर, यह रंग ग्रे में बदल जाता है:

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 पैकेज का दस्तावेज़ देखें.