Aralıklar

Oluşturma yöntemini deneyin
Android için önerilen kullanıcı arayüzü araç seti Jetpack Compose'dur. Oluştur'da metni nasıl kullanacağınızı öğrenin.

Aralıklar, metne karakter veya paragraf düzeyinde stil uygulamak için kullanabileceğiniz güçlü işaretleme nesneleridir. Metin nesnelerine aralık ekleyerek metni renk ekleme, metni tıklanabilir hale getirme, metin boyutunu ölçeklendirme ve metni özelleştirilmiş bir şekilde çizme gibi çeşitli şekillerde değiştirebilirsiniz. Aralıklar, TextPaint özelliklerini değiştirebilir, Canvas üzerinde çizim yapabilir ve metin düzenini değiştirebilir.

Android, çeşitli yaygın metin stili kalıplarını kapsayan çeşitli türde aralıklar sağlar. Özel stil uygulamak için kendi span'larınızı da oluşturabilirsiniz.

Aralık oluşturma ve uygulama

Aralık oluşturmak için aşağıdaki tabloda listelenen sınıflardan birini kullanabilirsiniz. Sınıflar, metnin kendisinin değişken olup olmadığına, metin işaretlemesinin değişken olup olmadığına ve aralığı içeren temel veri yapısına göre farklılık gösterir.

Sınıf Değiştirilebilir metin Değiştirilebilir işaretleme Veri yapısı
SpannedString Hayır Hayır Doğrusal dizi
SpannableString Hayır Evet Doğrusal dizi
SpannableStringBuilder Evet Evet Aralık ağacı

Üç sınıf da Spanned arayüzünü genişletir. SpannableString ve SpannableStringBuilder, Spannable arayüzünü de genişletir.

Hangisini kullanacağınıza nasıl karar vereceğiniz aşağıda açıklanmıştır:

  • Oluşturduktan sonra metni veya işaretlemeyi değiştirmiyorsanız SpannedString değerini kullanın.
  • Tek bir metin nesnesine az sayıda span eklemeniz gerekiyorsa ve metnin kendisi salt okunur durumdaysa SpannableString öğesini kullanın.
  • Metni oluşturduktan sonra değiştirmeniz ve metne aralıklar eklemeniz gerekiyorsa SpannableStringBuilder öğesini kullanın.
  • Metnin salt okunur olup olmadığına bakılmaksızın bir metin nesnesine çok sayıda span eklemeniz gerekiyorsa SpannableStringBuilder öğesini kullanın.

Aralık uygulamak için Spannable nesnesinde setSpan(Object _what_, int _start_, int _end_, int _flags_) işlevini çağırın. what parametresi, metne uyguladığınız aralığı ifade eder. start ve end parametreleri ise aralığı uyguladığınız metin bölümünü belirtir.

Bir aralığın sınırlarına metin eklerseniz aralığın sınırları, eklenen metni içerecek şekilde otomatik olarak genişler. Aralık sınırlarına (yani baş veya son dizelerinde) metin eklendiğinde flags parametresi, aralığın eklenen metni içerecek şekilde genişleyip genişlemeyeceğini belirler. Eklenen metni dahil etmek için Spannable.SPAN_EXCLUSIVE_INCLUSIVE işaretini, eklenen metni hariç tutmak için Spannable.SPAN_EXCLUSIVE_EXCLUSIVE işaretini kullanın.

Aşağıdaki örnekte, bir dizeye ForegroundColorSpan karakterinin nasıl ekleneceği gösterilmektedir:

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
);
Kısmen kırmızı olan gri bir metni gösteren resim.
Şekil 1. ForegroundColorSpan ile biçimlendirilmiş metin.

Aralık, Spannable.SPAN_EXCLUSIVE_INCLUSIVE kullanılarak ayarlandığından aşağıdaki örnekte gösterildiği gibi aralık sınırlarına eklenen metni içerecek şekilde genişler:

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 kullanıldığında span'ın nasıl daha fazla metin içerdiğini gösteren resim.
Şekil 2. Spannable.SPAN_EXCLUSIVE_INCLUSIVE kullanıldığında span, ek metin içerecek şekilde genişler.

Aynı metne birden fazla span ekleyebilirsiniz. Aşağıdaki örnekte, kalın ve kırmızı metnin nasıl oluşturulacağı gösterilmektedir:

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
);
Birden fazla span içeren bir metni gösteren resim: `ForegroundColorSpan(Color.RED)` ve `StyleSpan(BOLD)`
Şekil 3. Birden fazla aralığı kapsayan metin: ForegroundColorSpan(Color.RED) ve StyleSpan(BOLD).

Android span türleri

Android, android.text.style paketinde 20'den fazla span türü sağlar. Android, aralıkları iki temel şekilde kategorilere ayırır:

  • Aralık metni nasıl etkiler? Aralık, metnin görünümünü veya metin metriklerini etkileyebilir.
  • Aralık kapsamı: Bazı aralıklar tek tek karakterlere uygulanabilirken diğerleri bir paragrafın tamamına uygulanmalıdır.
Farklı kapsam kategorilerini gösteren bir resim
Şekil 4. Android aralıkları kategorileri.

Aşağıdaki bölümlerde bu kategoriler daha ayrıntılı olarak açıklanmaktadır.

Metin görünümünü etkileyen aralıklar

Karakter düzeyinde uygulanan bazı aralıklar metnin görünümünü etkiler (ör. metin veya arka plan rengini değiştirme, altı çizili veya üstü çizili ekleme). Bu span'lar CharacterStyle sınıfını genişletir.

Aşağıdaki kod örneğinde, metnin altını çizmek için UnderlineSpan karakterinin nasıl uygulanacağı gösterilmektedir:

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" kullanılarak metnin nasıl altının çizileceğini gösteren resim
Şekil 5. Metin, UnderlineSpan ile alt çizgili.

Yalnızca metin görünümünü etkileyen aralıklar, düzenin yeniden hesaplanmasını tetiklemeden metnin yeniden çizilmesini tetikler. Bu aralıklar UpdateAppearance'u uygular ve CharacterStyle'i genişletir. CharacterStyle alt sınıfları, TextPaint öğesini güncelleme erişimi sağlayarak metnin nasıl çizileceğini tanımlar.

Metin metriklerini etkileyen aralıklar

Karakter düzeyinde uygulanan diğer aralıklar, satır yüksekliği ve metin boyutu gibi metin metriklerini etkiler. Bu aralıklar, MetricAffectingSpan sınıfını genişletir.

Aşağıdaki kod örneği, metin boyutunu %50 artıran bir RelativeSizeSpan oluşturur:

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 öğesinin kullanımını gösteren resim
Şekil 6. RelativeSizeSpan kullanılarak büyütülmüş metin.

Metin metriklerini etkileyen bir aralığın uygulanması, gözlemleyen nesnenin doğru düzen ve oluşturma için metni yeniden ölçmesine neden olur. Örneğin, metin boyutunun değiştirilmesi kelimelerin farklı satırlarda görünmesine neden olabilir. Önceki aralığı uygulamak, metin düzeninin yeniden ölçülmesini, yeniden hesaplanmasını ve yeniden çizilmesini tetikler.

Metin metriklerini etkileyen aralıklar, MetricAffectingSpan sınıfını genişletir. Bu sınıf, alt sınıfların TextPaint erişimi sağlayarak aralığın metin ölçümünü nasıl etkilediğini tanımlamasına olanak tanıyan soyut bir sınıftır. MetricAffectingSpan, CharacterStyle'yi genişlettiğinden alt sınıflar, metnin karakter düzeyindeki görünümünü etkiler.

Paragrafları etkileyen aralıklar

Aralık, paragraf düzeyindeki metni de etkileyebilir (ör. bir metin bloğunun hizalamasını veya kenar boşluğunu değiştirme). Paragrafların tamamını etkileyen aralıklar ParagraphStyle'i uygular. Bu aralıkları kullanmak için son yeni satır karakteri hariç paragrafın tamamına eklersiniz. Bir paragrafın tamamı dışında bir yere paragraf aralığı uygulamaya çalışırsanız Android bu aralığı hiç uygulamaz.

Şekil 8'de, Android'in metindeki paragrafları nasıl ayırdığı gösterilmektedir.

Şekil 7. Android'de paragraflar yeni satır (\n) karakteriyle sona erer.

Aşağıdaki kod örneğinde, bir paragrafa QuoteSpan uygulanmaktadır. Aralık öğesini bir paragrafın başlangıcı veya sonu dışında bir konuma eklerseniz Android'in stili hiç uygulamadığını unutmayın.

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 örneğini gösteren resim
Şekil 8. Bir paragrafa uygulanan QuoteSpan

Özel aralıklar oluşturma

Mevcut Android span'larında sağlananlardan daha fazla işleve ihtiyacınız varsa özel bir span uygulayabilirsiniz. Kendi aralığınızı uygularken aralığınızın metni karakter düzeyinde mi yoksa paragraf düzeyinde mi etkilediğine ve ayrıca metnin düzenini mi yoksa görünümünü mü etkilediğine karar verin. Bu, hangi temel sınıfları genişletebileceğinizi ve hangi arayüzleri uygulamanız gerekebileceğini belirlemenize yardımcı olur. Referans olarak aşağıdaki tabloyu kullanın:

Senaryo Sınıf veya arayüz
Aralık, metni karakter düzeyinde etkiler. CharacterStyle
Aralık, metnin görünümünü etkiler. UpdateAppearance
Aralıkınız metin metriklerini etkiler. UpdateLayout
Aralık, metni paragraf düzeyinde etkiler. ParagraphStyle

Örneğin, metin boyutunu ve rengini değiştiren özel bir span uygulamanız gerekiyorsa RelativeSizeSpan öğesini genişletin. RelativeSizeSpan, devralma yoluyla CharacterStyle'ü genişletir ve iki Update arayüzünü uygular. Bu sınıf zaten updateDrawState ve updateMeasureState için geri çağırma işlevi sağladığından, özel davranışınızı uygulamak için bu geri çağırma işlevlerini geçersiz kılabilirsiniz. Aşağıdaki kod, RelativeSizeSpan öğesini genişleten ve TextPaint öğesinin rengini ayarlamak için updateDrawState geri çağırma işlevini geçersiz kılan özel bir span oluşturur:

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

Bu örnekte, özel bir aralığın nasıl oluşturulacağı gösterilmektedir. Metne RelativeSizeSpan ve ForegroundColorSpan uygulayarak da aynı efekti elde edebilirsiniz.

Aralık kullanımını test etme

Spanned arayüzü, hem aralıkları ayarlamanıza hem de metinden aralıkları almanıza olanak tanır. Test ederken, doğru aralıkların doğru konumlara eklendiğini doğrulamak için bir Android JUnit testi uygulayın. Metin Stili örnek uygulaması, metne BulletPointSpan ekleyerek madde işaretlerine işaretleme uygulayan bir span içerir. Aşağıdaki kod örneğinde, madde işaretlerinin beklendiği gibi görünüp görünmediğinin nasıl test edileceği gösterilmektedir:

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

Daha fazla test örneği için GitHub'daki MarkdownBuilderTest dosyasını inceleyin.

Özel aralıkları test etme

Aralıkları test ederken TextPaint öğesinin beklenen değişiklikleri içerdiğini ve Canvas öğenizin doğru öğeleri içerdiğini doğrulayın. Örneğin, bir metne madde işareti ekleyen özel bir span uygulaması düşünün. Noktanın boyutu ve rengi belirtilir. Ayrıca, çizilebilir alanın sol kenarlığı ile nokta arasında boşluk bulunur.

Bir AndroidJUnit testi uygulayarak bu sınıfın davranışını test edebilir ve aşağıdakileri kontrol edebilirsiniz:

  • Aralık aralığını doğru şekilde uygularsanız kanvasta belirtilen boyut ve renkte bir madde işareti görünür ve sol kenar boşluğu ile madde işareti arasında uygun boşluk bulunur.
  • Aralık uygulamazsanız özel davranışların hiçbiri görünmez.

Bu testlerin uygulanmasını GitHub'daki TextStyling örneğinde görebilirsiniz.

Tuvalin taklidini yaparak, taklit edilen nesneyi drawLeadingMargin() yöntemine ileterek ve doğru yöntemlerin doğru parametrelerle çağrıldığını doğrulayarak tuval etkileşimlerini test edebilirsiniz.

Daha fazla satır içi test örneğini BulletPointSpanTest dosyasında bulabilirsiniz.

Aralıkları kullanmayla ilgili en iyi uygulamalar

İhtiyaçlarınıza bağlı olarak, TextView içinde metin ayarlamak için bellek açısından verimli birkaç yöntem vardır.

Temel metni değiştirmeden bir aralığı ekleme veya kaldırma

TextView.setText() aralıkları farklı şekilde işleyen birden fazla aşırı yükleme içerir. Örneğin, aşağıdaki kodla bir Spannable metin nesnesi ayarlayabilirsiniz:

Kotlin

textView.setText(spannableObject)

Java

textView.setText(spannableObject);

setText() işlevinin bu aşırı yüklenmesini çağırırken TextView, Spannable değerinizin SpannedString olarak bir kopyasını oluşturur ve CharSequence olarak bellekte tutar. Bu, metninizin ve aralıkların değişmez olduğu anlamına gelir. Bu nedenle, metni veya aralıkları güncellemeniz gerektiğinde yeni bir Spannable nesnesi oluşturup setText() işlevini tekrar çağırmanız gerekir. Bu işlem, düzenin yeniden ölçülmesini ve yeniden çizilmesini de tetikler.

Aralıkların değiştirilebilir olması gerektiğini belirtmek için aşağıdaki örnekte gösterildiği gibi setText(CharSequence text, TextView.BufferType type) kullanabilirsiniz:

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

Bu örnekte, BufferType.SPANNABLE parametresi TextView'ın bir SpannableString oluşturmasına neden olur ve TextView tarafından tutulan CharSequence nesnesinde artık değiştirilebilir işaretleme ve değiştirilemez metin bulunur. Aralıkları güncellemek için metni Spannable olarak alın ve ardından aralıkları gerektiği gibi güncelleyin.

Aralıkları eklediğinizde, kaldırdığınızda veya yeniden konumlandırdığınızda TextView, metindeki değişikliği yansıtacak şekilde otomatik olarak güncellenir. Mevcut bir aralığın dahili özelliğini değiştirirseniz görünümle ilgili değişiklikler yapmak için invalidate()'ü veya metriklerle ilgili değişiklikler yapmak için requestLayout()'u çağırın.

TextView'de metni birden çok kez ayarlama

RecyclerView.ViewHolder kullanırken olduğu gibi bazı durumlarda TextView öğesini yeniden kullanmak ve metni birden çok kez ayarlamak isteyebilirsiniz. Varsayılan olarak, BufferType ayarlanıp ayarlanmadığından bağımsız olarak TextView, CharSequence nesnesinin bir kopyasını oluşturur ve bellekte tutar. Bu, tüm TextView güncellemelerini kasıtlı hale getirir. Metni güncellemek için orijinal CharSequence nesnesini güncelleyemezsiniz. Yani her yeni metin ayarladığınızda TextView yeni bir nesne oluşturur.

Bu süreç üzerinde daha fazla kontrol sahibi olmak ve fazladan nesne oluşturmaktan kaçınmak istiyorsanız kendi Spannable.Factory öğenizi uygulayabilir ve newSpannable() öğesini geçersiz kılabilirsiniz. Yeni bir metin nesnesi oluşturmak yerine, aşağıdaki örnekte gösterildiği gibi mevcut CharSequence öğesini yayınlayıp Spannable olarak döndürebilirsiniz:

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

Metni ayarlarken textView.setText(spannableObject, BufferType.SPANNABLE) kullanmanız gerekir. Aksi takdirde, kaynak CharSequence bir Spanned örneği olarak oluşturulur ve Spannable olarak yayınlayamaz. Bu da newSpannable()'ın bir ClassCastException atmasına neden olur.

newSpannable()'ü geçersiz kıldıktan sonra TextView'a yeni Factory'yi kullanmasını söyleyin:

Kotlin

textView.setSpannableFactory(spannableFactory)

Java

textView.setSpannableFactory(spannableFactory);

TextView nesnesini, TextView öğenize referans aldıktan hemen sonra bir kez ayarlayın.Spannable.Factory RecyclerView kullanıyorsanız görüntülemelerinizi ilk kez şişirirken Factory nesnesini ayarlayın. Bu sayede, RecyclerView ViewHolder'unuza yeni bir öğe bağladığında fazladan nesne oluşturulmaz.

Dahili span özelliklerini değiştirme

Değiştirilebilir bir span'ın yalnızca dahili bir özelliğini (ör. özel bir span'daki madde işareti rengi) değiştirmeniz gerekiyorsa span oluşturulduğunda span'a referans tutarak setText() işlevinin birden çok kez çağrılmasından kaynaklanan yükü önleyebilirsiniz. Aralıkta değişiklik yapmanız gerektiğinde referansı değiştirebilir ve ardından, değiştirdiğiniz özelliğin türüne bağlı olarak TextView üzerinde invalidate() veya requestLayout()'yi çağırabilirsiniz.

Aşağıdaki kod örneğinde, özel bir madde işareti uygulamasının varsayılan rengi kırmızıdır ve düğmeye dokunulduğunda gri olur:

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 uzantı işlevlerini kullanma

Android KTX, aralıkları kullanmayı kolaylaştıran uzantı işlevleri de içerir. Daha fazla bilgi edinmek için androidx.core.text paketinin belgelerini inceleyin.