স্প্যান

Compose-এর মাধ্যমে চেষ্টা করুন
Jetpack Compose হল Android-এর জন্য সাজেস্ট করা UI টুলকিট। 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
)

জাভা

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)")

জাভা

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
)

জাভা

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 প্যাকেজে ২০টির বেশি স্প্যান টাইপ প্রদান করে। Android মূলত দুটি উপায়ে স্প্যানকে শ্রেণীবদ্ধ করে:

  • স্প্যান কীভাবে টেক্সটকে প্রভাবিত করে: স্প্যান টেক্সটের উপস্থিতি বা টেক্সট মেট্রিক্সকে প্রভাবিত করতে পারে।
  • স্প্যান স্কোপ: কিছু স্প্যান আলাদা আলাদা অক্ষরে প্রয়োগ করা যেতে পারে, আবার অন্যগুলি পুরো অনুচ্ছেদে প্রয়োগ করতে হবে।
বিভিন্ন স্প্যাম বিভাগ দেখানো একটি ছবি
ছবি ৪. Android স্প্যানের বিভাগ।

নিম্নলিখিত বিভাগগুলি আরও বিস্তারিতভাবে এই বিভাগগুলি বর্ণনা করে।

টেক্সটের চেহারায় প্রভাব ফেলে এমন স্প্যান

কিছু স্প্যান যা অক্ষর লেভেলে প্রয়োগ করা হয় তা টেক্সটের চেহারাকে প্রভাবিত করে, যেমন টেক্সট বা ব্যাকগ্রাউন্ডের রঙ পরিবর্তন করা এবং আন্ডারলাইন বা স্ট্রাইকথ্রু যোগ করা। এই স্প্যানগুলি CharacterStyle ক্লাসকে এক্সটেন্ড করে।

নিচের কোডের উদাহরণ থেকে জানতে পারবেন যে কীভাবে টেক্সট আন্ডারলাইন করার জন্য UnderlineSpan প্রয়োগ করতে হয়:

Kotlin

val string = SpannableString("Text with underline span")
string.setSpan(UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

জাভা

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 তৈরি করে যা টেক্সটের সাইজ ৫০% বাড়িয়ে দেয়:

Kotlin

val string = SpannableString("Text with relative size span")
string.setSpan(RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

জাভা

SpannableString string = new SpannableString("Text with relative size span");
string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
RelativeSizeSpan-এর ব্যবহার দেখানো একটি ছবি
ছবি ৬. RelativeSizeSpan ব্যবহার করে টেক্সট বড় করা হয়েছে।

টেক্সট মেট্রিক্সকে প্রভাবিত করে এমন স্প্যান প্রয়োগ করলে, সঠিক লেআউট ও রেন্ডারিংয়ের জন্য পর্যবেক্ষণকারী অবজেক্ট টেক্সটকে আবার পরিমাপ করে—যেমন, টেক্সটের সাইজ পরিবর্তন করলে শব্দগুলি আলাদা আলাদা লাইনে দেখা যেতে পারে। পূর্ববর্তী span প্রয়োগ করলে টেক্সট লেআউট আবার পরিমাপ ও গণনা করা হয় এবং টেক্সট আবার আঁকা হয়।

টেক্সট মেট্রিককে প্রভাবিত করে এমন স্প্যান MetricAffectingSpan ক্লাসকে এক্সটেন্ড করে, এটি একটি অ্যাবস্ট্রাক্ট ক্লাস যা সাবক্লাসকে TextPaint-এ অ্যাক্সেস প্রদান করে স্প্যান কীভাবে টেক্সট পরিমাপকে প্রভাবিত করে তা নির্ধারণ করতে দেয়। যেহেতু MetricAffectingSpan CharacterStyle-কে এক্সটেন্ড করে, তাই সাবক্লাসগুলি ক্যারেক্টার লেভেলে টেক্সটের অ্যাপিয়ারেন্সকে প্রভাবিত করে।

প্যারাগ্রাফকে প্রভাবিত করে এমন স্প্যান

স্প্যান প্যারাগ্রাফ লেভেলে টেক্সটকেও প্রভাবিত করতে পারে, যেমন টেক্সটের ব্লকের অ্যালাইনমেন্ট বা মার্জিন পরিবর্তন করা। সম্পূর্ণ অনুচ্ছেদকে প্রভাবিত করে এমন স্প্যান ParagraphStyle প্রয়োগ করে। এইসব স্প্যান ব্যবহার করতে, আপনি সেগুলিকে সম্পূর্ণ অনুচ্ছেদে অ্যাটাচ করেন, যার মধ্যে শেষের নতুন লাইন অক্ষর অন্তর্ভুক্ত থাকে না। আপনি যদি সম্পূর্ণ অনুচ্ছেদ ছাড়া অন্য কোনও কিছুতে অনুচ্ছেদ স্প্যান প্রয়োগ করার চেষ্টা করেন, তাহলে Android স্প্যানটি একেবারেই প্রয়োগ করে না।

Android কীভাবে টেক্সটে প্যারাগ্রাফ আলাদা করে তা ছবি ৮-এ দেখানো হয়েছে।

ছবি ৭. Android-এ, অনুচ্ছেদ নতুন লাইন (\n) অক্ষর দিয়ে শেষ হয়।

নিম্নলিখিত কোডের উদাহরণ একটি প্যারাগ্রাফে QuoteSpan প্রয়োগ করে। মনে রাখবেন যে আপনি যদি কোনও অনুচ্ছেদের শুরু বা শেষ ছাড়া অন্য কোনও অবস্থানে স্প্যানটি অ্যাটাচ করেন, তাহলে Android স্টাইলটি একেবারেই প্রয়োগ করে না।

Kotlin

spannable.setSpan(QuoteSpan(color), 8, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

জাভা

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
    }
}

জাভা

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())
}

জাভা

@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)

জাভা

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
)

জাভা

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)

জাভা

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()
        }
    }
}

জাভা

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 প্যাকেজের ডকুমেন্টেশন দেখুন।