स्पैन

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

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

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

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

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

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

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

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

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

स्पैन लागू करने के लिए, Spannable ऑब्जेक्ट पर setSpan(Object _what_, int _start_, int _end_, int _flags_) कॉल करें. what पैरामीटर से उस स्पैन के बारे में पता चलता है जिसे टेक्स्ट पर लागू किया जा रहा है. साथ ही, 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
);
ऐसी इमेज जिसमें ग्रे रंग का टेक्स्ट है और कुछ हिस्से में लाल रंग है.
पहली इमेज. टेक्स्ट को 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 का इस्तेमाल करने पर, स्पैन में ज़्यादा टेक्स्ट कैसे शामिल होता है.
दूसरी इमेज. 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)`
तीसरी इमेज. एक से ज़्यादा स्पैन वाला टेक्स्ट: ForegroundColorSpan(Color.RED) और StyleSpan(BOLD).

Android स्पैन टाइप

Android, android.text.style पैकेज में 20 से ज़्यादा तरह के स्पैन उपलब्ध कराता है. Android, स्पैन को दो मुख्य तरीकों से बांटता है:

  • स्पैन का टेक्स्ट पर क्या असर पड़ता है: स्पैन का टेक्स्ट के दिखने या टेक्स्ट की मेट्रिक पर असर पड़ सकता है.
  • स्पैन का दायरा: कुछ स्पैन, अलग-अलग वर्णों पर लागू किए जा सकते हैं, जबकि अन्य को पूरे पैराग्राफ़ पर लागू करना ज़रूरी है.
अलग-अलग स्पैन कैटगरी दिखाने वाली इमेज
चौथी इमेज. 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` का इस्तेमाल करके टेक्स्ट को अंडरलाइन करने का तरीका बताने वाली इमेज
पांचवीं इमेज. टेक्स्ट के नीचे रेखा लगाने के लिए, 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 का इस्तेमाल दिखाने वाली इमेज
छठी इमेज. टेक्स्ट को बड़ा करने के लिए, RelativeSizeSpan का इस्तेमाल किया गया.

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

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

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

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

आठवें चित्र में दिखाया गया है कि Android, टेक्स्ट में पैराग्राफ़ को कैसे अलग करता है.

सातवीं इमेज. 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 का उदाहरण दिखाने वाली इमेज
आठवां डायग्राम. किसी पैराग्राफ़ पर लागू किया गया 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));
}

जांच के ज़्यादा उदाहरणों के लिए, GitHub पर MarkdownBuilderTest देखें.

कस्टम स्पैन की जांच करना

स्पैन की जांच करते समय, पुष्टि करें कि 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 का फिर से इस्तेमाल करना हो और टेक्स्ट को कई बार सेट करना हो. डिफ़ॉल्ट रूप से, TextView, CharSequence ऑब्जेक्ट की कॉपी बनाता है और उसे मेमोरी में सेव रखता है. भले ही, आपने BufferType को सेट किया हो या नहीं. इससे सभी 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 पैकेज का दस्तावेज़ देखें.