Aralıklar

Oluşturma yöntemini deneyin
Jetpack Compose, Android için önerilen kullanıcı arayüzü araç setidir. Compose'da metin kullanmayı öğrenin.

Aralıklar, metni karakter veya paragraf düzeyinde biçimlendirmek için kullanabileceğiniz güçlü işaretleme nesneleridir. Metin nesnelerine kapsam ekleyerek, renk eklemek, metni tıklanabilir yapmak, metin boyutunu ölçeklendirmek ve metni özelleştirilmiş bir şekilde çizmek gibi çeşitli şekillerde metni değiştirebilirsiniz. Aralıklar ayrıca TextPaint özelliklerini değiştirebilir, Canvas üzerinde çizim yapabilir ve metin düzenini değiştirebilir.

Android, yaygın olarak kullanılan çeşitli metin stili kalıplarını kapsayan çeşitli aralıklar sunar. Özel stil uygulamak için kendi kapsamlarınızı da oluşturabilirsiniz.

Kapsam oluşturma ve uygulama

Aralık oluşturmak için aşağıdaki tabloda listelenen sınıflardan birini kullanabilirsiniz. Sınıflar; metnin değişken olup olmamasına, metin işaretlemenin değişken olup olmamasına ve hangi temel veri yapısının aralık verilerini içerdiğine bağlı olarak farklılık gösterir.

Sınıf Değişebilir metin Değişebilir 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 ayrıca Spannable arayüzünü genişletir.

Hangisinin kullanılacağına nasıl karar vereceğiniz aşağıda açıklanmıştır:

  • Oluşturma işleminden sonra metni veya işaretlemeyi değiştirmezseniz SpannedString değerini kullanın.
  • Tek bir metin nesnesine az sayıda aralık eklemeniz gerekiyorsa ve metnin kendisi salt okunursa SpannableString işlevini kullanın.
  • Metni oluşturduktan sonra değiştirmeniz ve metne kapsam eklemeniz gerekirse SpannableStringBuilder kullanın.
  • Bir metin nesnesine çok sayıda aralık eklemeniz gerekirse, metnin salt okunur olup olmadığından bağımsız olarak SpannableStringBuilder işlevini kullanın.

Kapsamı uygulamak için bir Spannable nesnesinde setSpan(Object _what_, int _start_, int _end_, int _flags_) çağrısı yapın. what parametresi, metne uyguladığınız aralığı belirtir. start ve end parametreleri ise metnin aralığı uyguladığınız bölümünü belirtir.

Bir kapsamın sınırlarının içine metin eklerseniz, aralık eklenen metni içerecek şekilde otomatik olarak genişler. Aralık sınırlarına (yani başlangıç veya bitiş dizinlerine) metin eklerken flags parametresi, span öğesinin eklenen metni içerecek şekilde genişletilip genişletilmeyeceğ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, dizeye bir ForegroundColorSpan öğesinin 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ı olmak üzere gri bir metnin gösterildiği resim.
Şekil 1. Metin stili ForegroundColorSpan ile ayarlandı.

Aralık, Spannable.SPAN_EXCLUSIVE_INCLUSIVE kullanılarak ayarlandığından, kapsam, 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 aralığın nasıl daha fazla metin içerdiğini gösteren resim.
Şekil 2. Aralık, Spannable.SPAN_EXCLUSIVE_INCLUSIVE kullanılırken ek metin içerecek şekilde genişler.

Aynı metne birden çok kapsam ekleyebilirsiniz. Aşağıdaki örnekte kalın ve kırmızı metinlerin 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 çok aralıklı metni gösteren resim: "ForegroundColorSpan(Color.RED)" ve "StyleSpan(BOLD)"
Şekil 3. Birden çok aralıklı metin: ForegroundColorSpan(Color.RED) ve StyleSpan(BOLD).

Android aralık türleri

Android, android.text.style paketinde 20'den fazla aralık türü sağlar. Android, aralıkları iki temel şekilde sınıflandırır:

  • Aralık, metni nasıl etkiler: Bir kapsam, metin görünümünü veya metin metriklerini etkileyebilir.
  • Genişleme kapsamı: Bazı aralıklar tek tek karakterlere uygulanabilirken bazılarının da bir paragrafın tamamına uygulanması gerekir.
Farklı aralık kategorilerini gösteren resim
Şekil 4. Android aralıklarının 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ı kapsamlar, metin veya arka plan rengini değiştirme ve alt çizgi ya da üstü çizili ekleme gibi metin görünümünü etkiler. Bu aralıklar, CharacterStyle sınıfını genişletir.

Aşağıdaki kod örneğinde, metnin altını çizmek için nasıl UnderlineSpan 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" kullanarak metnin altının nasıl çizileceğini gösteren resim
Şekil 5. Metnin altı, UnderlineSpan kullanılarak çizildi.

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'yi uygular ve CharacterStyle'i genişletir. CharacterStyle alt sınıfları, TextPaint güncelleme erişimi sağlayarak metnin nasıl çizileceğini tanımlar.

Metin metriklerini etkileyen aralıklar

Karakter düzeyinde geçerli olan diğer kapsamlar, 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şturmaktadır:

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);
GöreliSizeSpan kullanımını gösteren resim
Şekil 6. Metin, RelativeSizeSpan kullanılarak büyütüldü.

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

Metin metriklerini etkileyen aralıklar MetricAffectingSpan sınıfını genişletir. Bu soyut sınıf, alt sınıfların TextPaint öğesine erişim sağlayarak aralığın metin ölçümünü nasıl etkilediğini tanımlamasına olanak tanır. MetricAffectingSpan, CharacterSpan kapsamını genişlettiği için alt sınıflar metnin karakter düzeyinde görünümünü etkiler.

Paragrafları etkileyen aralıklar

Kapsama alanı, metni paragraf düzeyinde de etkileyebilir. Örneğin, bir metin bloğunun hizalamasını veya kenar boşluğunu değiştirebilir. Paragrafların tamamını etkileyen aralıklar ParagraphStyle uygulayın. Bu aralıkları kullanmak için, bunları sondaki yeni satır karakterini hariç tutarak paragrafın tamamına eklersiniz. Bir paragraf kapsamını paragrafın tamamı dışındaki bir şeye uygulamaya çalışırsanız Android, aralığı hiç uygulamaz.

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

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

Aşağıdaki kod örneği, bir paragrafa QuoteSpan uygular. Kapsamı, bir paragrafın başı veya sonu dışındaki herhangi 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 kapsam oluşturma

Mevcut Android aralıklarında sağlanandan daha fazla işleve ihtiyacınız varsa özel bir kapsam uygulayabilirsiniz. Kendi aralığınızı uygularken, kapsamınızın metni karakter düzeyinde mi yoksa paragraf düzeyinde mi etkileyeceğine ve metnin düzenini veya görünümünü etkileyip etkilemediğ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 için aşağıdaki tabloyu kullanın:

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

Örneğin, metin boyutunu ve rengini değiştiren bir özel kapsam uygulamanız gerekiyorsa RelativeSizeSpan öğesini genişletin. Devralma yoluyla RelativeSizeSpan, CharacterStyle öğesini genişletir ve iki Update arayüzünü uygular. Bu sınıf, updateDrawState ve updateMeasureState için zaten geri çağırma işlevi sağladığından özel davranışınızı uygulamak için bu geri çağırmaları geçersiz kılabilirsiniz. Aşağıdaki kod RelativeSizeSpan öğesini genişleten ve TextPaint rengini ayarlamak için updateDrawState geri çağırmasını geçersiz kılan özel bir kapsam 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ı etkiyi elde edebilirsiniz.

Test aralığı kullanımı

Spanned arayüzü, hem aralıkları ayarlamanıza hem de metinden aralıkları almanıza olanak tanır. Test sırasında 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 aralık içerir. Aşağıdaki kod örneğinde, madde işaretlerinin beklendiği gibi görünüp görünmediğini nasıl test edeceğiniz 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'da MarkdownBuilderTest bölümüne bakın.

Özel aralıkları test etme

Aralıkları test ederken TextPaint öğesinin beklenen değişiklikleri içerdiğini ve Canvas üzerinde doğru öğelerin göründüğünü doğrulayın. Örneğin, bir metnin başına madde işareti ekleyen özel bir aralık uygulaması düşünün. Madde işareti belirli bir boyut ve renktedir ve çekilebilir alanın sol kenar boşluğu ile madde işareti arasında boşluk vardır.

Bir AndroidJUnit testi uygulayıp aşağıdakileri kontrol ederek bu sınıfın davranışını test edebilirsiniz:

  • Aralığı doğru şekilde uygularsanız kanvasta, belirtilen boyutta 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.
  • Kapsamı uygulamazsanız özel davranışların hiçbiri görünmez.

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

Tuvalle dalga yaparak, sahte nesneyi drawLeadingMargin() yöntemine ileterek ve doğru yöntemlerin doğru parametrelerle çağrıldığını doğrulayarak Canvas etkileşimlerini test edebilirsiniz.

Daha fazla aralık testi örneğini BulletPointSpanTest'te bulabilirsiniz.

Aralık kullanımıyla ilgili en iyi uygulamalar

İhtiyaçlarınıza bağlı olarak, TextView içinde metin ayarlamanın belleği verimli kullanan çeşitli yolları vardır.

Alttaki metni değiştirmeden kapsam ekleme veya çıkarma

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

Kotlin

textView.setText(spannableObject)

Java

textView.setText(spannableObject);

Bu setText() aşırı yüklemesi çağrılırken TextView, Spannable öğenizin SpannedString olarak bir kopyasını oluşturur ve CharSequence olarak bellekte tutar. Bu, metninizin ve kapsamlarınızın sabit olduğu anlamına gelir. Bu nedenle, metni veya kapsamları güncellemeniz gerektiğinde yeni bir Spannable nesnesi oluşturup setText() öğesini tekrar çağırın. Bu işlem, düzenin yeniden ölçülmesini ve yeniden çizilmesini de tetikler.

Aralıkların değişken olması gerektiğini belirtmek için aşağıdaki örnekte gösterildiği gibi setText(CharSequence text, TextView.BufferType type) işlevini 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 öğesinin bir SpannableString oluşturmasına neden olur ve TextView tarafından saklanan CharSequence nesnesi artık değişken işaretlemeye ve sabit metne sahiptir. Kapsamı güncellemek için metni Spannable olarak alın ve ardından aralıkları gerektiği gibi güncelleyin.

Aralıkları eklediğinizde, çıkardığı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 metrikle ilgili değişiklikler yapmak için requestLayout() çağrısı yapın.

TextView'da metni birden çok kez ayarlama

RecyclerView.ViewHolder kullanımı gibi bazı durumlarda, bir TextView öğesini yeniden kullanıp metni birden çok kez ayarlamak isteyebilirsiniz. Varsayılan olarak, BufferType öğesini ayarlayıp ayarlamamanızdan bağımsız olarak TextView, CharSequence nesnesinin bir kopyasını oluşturur ve bunu bellekte tutar. Bu, tüm TextView güncellemelerini bilinçli hale getirir. Metni güncellemek için orijinal CharSequence nesnesini güncelleyemezsiniz. Bu, her yeni metin oluşturduğunuzda, TextView öğesinin yeni bir nesne oluşturduğu anlamına gelir.

Bu süreç üzerinde daha fazla kontrol sahibi olmak ve fazladan nesne oluşturulmasını önlemek istiyorsanız kendi Spannable.Factory kodunuzu uygulayabilir ve newSpannable() özelliğini geçersiz kılabilirsiniz. Yeni bir metin nesnesi oluşturmak yerine, aşağıdaki örnekte gösterildiği gibi mevcut CharSequence öğesini Spannable olarak yayınlayabilir ve 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 CharSequence kaynağı, Spanned örneği olarak oluşturulur ve Spannable'e yayınlanamaz. Bu da newSpannable()'un ClassCastException döndürmesine neden olur.

newSpannable() geçersiz kılındıktan sonra, TextView ürününe yeni Factory öğesini kullanmasını söyleyin:

Kotlin

textView.setSpannableFactory(spannableFactory)

Java

textView.setSpannableFactory(spannableFactory);

Spannable.Factory nesnesini bir kez, TextView için referans aldıktan hemen sonra ayarlayın. RecyclerView kullanıyorsanız görünümleri ilk kez şişirirken Factory nesnesini ayarlayın. Bu, RecyclerView cihazınız ViewHolder öğesine yeni bir öğe bağlarken fazladan nesne oluşturulmasını önler.

Dahili span özelliklerini değiştirme

Değişken bir aralığın yalnızca dahili bir özelliğini (ör. özel madde işareti aralığındaki madde işareti rengi) değiştirmeniz gerekiyorsa, aralık oluşturulduğu sırada bu aralık için bir referans tutarak setText() çağrısının birden fazla kez yapılmasını engellemek için ek yükten kaçınabilirsiniz. Aralığı değiştirmeniz gerektiğinde, referansı değiştirebilir ve daha sonra, değiştirdiğiniz özelliğin türüne bağlı olarak TextView için invalidate() veya requestLayout() işlevini çağırabilirsiniz.

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

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ıklarla çalışmayı kolaylaştıran uzantı işlevleri de içerir. Daha fazla bilgi edinmek için androidx.core.text paketinin belgelerine bakın.