स्पैन

लिखने की सुविधा आज़माएं
Android के लिए, Jetpack 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 पैरामीटर, टेक्स्ट के उस हिस्से को दिखाते हैं जिस पर आपको स्पैन लागू करना है.

अगर स्पैन की सीमाओं के अंदर कोई टेक्स्ट डाला जाता है, तो स्पैन अपने-आप बड़ा हो जाता है, ताकि डाले गए टेक्स्ट को शामिल किया जा सके. स्पैन की सीमाओं पर टेक्स्ट डालने पर—यानी कि शुरू या आखिर के इंडेक्स पर—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)");
इस इमेज में दिखाया गया है कि 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 सबक्लास यह तय करते हैं कि 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 को बढ़ाता है और 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 इंटरफ़ेस की मदद से, स्पैन सेट किए जा सकते हैं. साथ ही, टेक्स्ट से स्पैन वापस पाए जा सकते हैं. जांच करते समय, 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 sample में, इन टेस्ट को लागू करने का तरीका देखा जा सकता है.

कैनवस को मॉक करके, कैनवस इंटरैक्शन की जांच की जा सकती है. इसके लिए, मॉक किए गए ऑब्जेक्ट को 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);

Spannable.Factory ऑब्जेक्ट को एक बार सेट करें. ऐसा तब करें, जब आपको अपने TextView का रेफ़रंस मिल जाए. अगर 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 पैकेज का दस्तावेज़ देखें.