Aralıklar

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

Boşluklar, karakter veya paragraf düzeyinde metin stili belirlemek için kullanabileceğiniz güçlü işaretleme nesneleridir. Metin nesnelerine aralık ekleyerek metni renk ekleme, tıklanabilir hale getirme, metin boyutunu ölçeklendirme ve özelleştirilmiş bir şekilde metin çizme gibi çeşitli yollarla değiştirebilirsiniz. Kapsamlar 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ık türleri sağlar. Özel stil uygulamak için kendi aralıklarınızı da oluşturabilirsiniz.

Kapsam oluşturma ve uygulama

Kapsam oluşturmak için aşağıdaki tabloda listelenen sınıflardan birini kullanabilirsiniz. Sınıflar; metnin kendini değiştirebilir olup olmadığına, metin işaretlemesinin değiştirilebilir olup olmadığına ve span verilerini içeren temel veri yapısına göre 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, Spannable arayüzünü de genişletir.

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

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

Aralık 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ığı, start ve end parametreleri ise metnin aralığı uyguladığınız bölümünü belirtir.

Bir kapsamın sınırları içine metin eklerseniz aralık, eklenen metni içerecek şekilde otomatik olarak genişler. Aralık sınırlarına (yani start veya end dizinlerine) metin eklerken flags parametresi, kapsamın eklenen metni içerecek şekilde genişleyip genişletilmeyeceğini belirler. Eklenen metni dahil etmek için Spannable.SPAN_EXCLUSIVE_INCLUSIVE işaretini, hariç tutulan metni hariç tutmak için Spannable.SPAN_EXCLUSIVE_EXCLUSIVE işaretini kullanın.

Aşağıdaki örnekte bir dizeye ForegroundColorSpan özelliğinin 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 metin gösteren resim.
Şekil 1. Metnin stili ForegroundColorSpan ile ayarlandı.

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

Aynı metne birden çok aralık 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ık içeren bir metin 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 kategorilere ayırır:

  • Yayılım, metni nasıl etkiler? Kapsama alanı, metin görünümünü veya metin metriklerini etkileyebilir.
  • Genişleme kapsamı: Bazı aralıklar tek tek karakterlere, bazıları ise bir paragrafın tamamına uygulanabilir.
Farklı aralık kategorilerini gösteren resim
Şekil 4. Android aralıkları.

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

Metnin veya arka plan renginin değiştirilmesi, altı çizili ya da üstü çizili ekleme gibi karakter düzeyinde uygulanan bazı kapsamlar 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 UnderlineSpan özelliğinin 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);
"Alt çizgiliSpan" kullanarak metnin altını nasıl çizeceğinizi gösteren resim
Şekil 5. Metnin altı UnderlineSpan kullanılarak çizilir.

Yalnızca metin görünümünü etkileyen aralıklar, düzenin yeniden hesaplanmasına neden olmadan metnin yeniden çizilmesini tetikler. Bu aralıklar UpdateAppearance uygular ve CharacterStyle öğesini genişletir. CharacterStyle alt sınıfları, TextPaint öğesini güncelleme erişimi sağlayarak nasıl metin ç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);
FocusSizeSpan kullanımını gösteren resim
Şekil 6. Metin, RelativeSizeSpan kullanılarak büyütüldü.

Metin metriklerini etkileyen bir aralık uygulamak, gözlem yapan bir 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 kapsamın uygulanması yeniden ölçüm yapılmasını, metin düzeninin yeniden hesaplanmasını ve metin 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 özelliğine erişim sağlayarak aralığın metin ölçümünü nasıl etkileyeceğini tanımlamasına olanak tanır. MetricAffectingSpan, CharacterSpan öğesini kapsadığından alt sınıflar metnin görünümünü karakter düzeyinde etkiler.

Paragrafları etkileyen aralıklar

Yayılma, bir metin bloğunun hizalamasını veya kenar boşluğunu değiştirmek gibi paragraf düzeyindeki metni de etkileyebilir. Tüm paragrafları etkileyen aralıklarda ParagraphStyle uygulanır. Bu aralıkları kullanmak için bunları, bitişteki yeni satır karakteri hariç tüm paragrafa eklersiniz. Bir paragraf aralığı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. Aralığı bir paragrafın başlangıcı veya sonu dışında herhangi bir konuma eklerseniz Android'in bu 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 bir resim
Şekil 8. Paragrafa uygulanan QuoteSpan.

Özel aralıklar oluşturma

Mevcut Android aralıklarında sağlanandan daha fazla işleve ihtiyacınız varsa özel bir aralık uygulayabilirsiniz. Kendi aralığınızı uygularken aralığınızın metni karakter düzeyinde mi yoksa paragraf düzeyinde mi etkileyeceğine ve ayrıca 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
Aralığınız metin görünümünü etkiler. UpdateAppearance
Aralığınız metin metriklerini etkiler. UpdateLayout
Aralığınız, paragraf düzeyindeki metni etkiler. ParagraphStyle

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

Aralık kullanımını test et

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 Styling ö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 konusuna bakın.

Özel aralıkları test etme

Test kapsamını test ederken TextPaint öğesinin beklenen değişiklikleri içerdiğini ve Canvas cihazınızda doğru öğelerin göründüğünü doğrulayın. Örneğin, bir metnin başına madde işareti ekleyen özel aralık uygulamasını değerlendirebilirsiniz. Madde işareti noktasının belirli bir boyutu ve rengi var ve çekilebilir alanın sol kenar boşluğuyla madde işareti arasında boşluk var.

Bu sınıfın davranışını, AndroidJUnit testi uygulayarak ve aşağıdakileri kontrol ederek test edebilirsiniz:

  • Kapsamı doğru şekilde uygularsanız zeminde 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 nasıl uygulandığını GitHub'daki TextStyling örneğinde görebilirsiniz.

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

BulletPointSpanTest'te daha fazla aralık testi örneği bulabilirsiniz.

Aralıkları kullanmayla ilgili en iyi uygulamalar

İhtiyaçlarınıza bağlı olarak, bir TextView içinde metin ayarlamanın bellek açısından verimli birkaç yolu vardır.

Temel metni değiştirmeden bir yayılma metni ekleme veya çıkarma

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

Kotlin

textView.setText(spannableObject)

Java

textView.setText(spannableObject);

Bu aşırı setText() yüklemesi çağrılırken TextView, Spannable öğenizin bir SpannedString kopyasını oluşturur ve CharSequence olarak bellekte tutar. Bu, metninizin ve aralıkların sabit olduğu anlamına gelir. Bu nedenle, metni veya aralıkları güncellemeniz gerektiğinde yeni bir Spannable nesnesi oluşturup setText() çağrısını tekrar yapabilirsiniz. Bu, 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 öğesinin bir SpannableString oluşturmasına neden oluyor ve TextView tarafından tutulan CharSequence nesnesi artık değişebilir işaretleme ve sabit metne sahip. Kapsamı güncellemek için metni Spannable olarak alın ve ardından aralıkları gerektiği şekilde güncelleyin.

Aralıkları eklediğinizde, ayırdığınızda veya yeniden konumlandırdığınızda TextView, metinde yapılan değişikliği yansıtacak şekilde otomatik olarak güncellenir. Mevcut bir kapsamın dahili özelliğini değiştirirseniz görünümle ilgili değişiklikler yapmak için invalidate() yöntemini veya metrikle ilgili değişiklikler yapmak için requestLayout() yöntemini çağırın.

TextView'da metni birden çok kez ayarlama

RecyclerView.ViewHolder kullanmak gibi bazı durumlarda, bir TextView öğesini yeniden kullanmak ve metni birden çok kez ayarlamak isteyebilirsiniz. Varsayılan olarak, BufferType özelliğini ayarlayıp ayarlamamanızdan bağımsız olarak TextView, CharSequence nesnesinin bir kopyasını oluşturur ve bellekte tutar. Bu durum, tüm TextView güncellemelerinin kasıtlı olarak gerçekleştirilmesine neden olur. Metni güncellemek için orijinal CharSequence nesnesini güncelleyemezsiniz. Bu, her yeni metin ayarladığınızda TextView öğesinin yeni bir nesne oluşturduğu anlamına gelir.

Bu işlem üzerinde daha fazla kontrol sahibi olmak ve fazladan nesne oluşturulmasından kaçınmak isterseniz kendi Spannable.Factory uygulamanızı uygulayabilir ve newSpannable() öğesini geçersiz kılabilirsiniz. Yeni bir metin nesnesi oluşturmak yerine, mevcut CharSequence öğesini aşağıdaki örnekte gösterildiği gibi 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 öğesine dönüştürülemez. Bu durumda newSpannable(), ClassCastException hatası verebilir.

newSpannable() öğesini geçersiz kıldıktan sonra TextView adlı kullanıcıdan yeni Factory öğesini kullanmasını isteyin:

Kotlin

textView.setSpannableFactory(spannableFactory)

Java

textView.setSpannableFactory(spannableFactory);

Spannable.Factory nesnesini bir kez, TextView referansı aldıktan hemen sonra ayarlayın. RecyclerView kullanıyorsanız görünümlerinizi ilk kez artırırken Factory nesnesini ayarlayın. Bu sayede, RecyclerView cihazınız ViewHolder cihazınıza yeni bir öğe bağladığında ekstra nesne oluşturulması önlenmiş olur.

Dahili span özelliklerini değiştirme

Değişebilir aralığın yalnızca dahili bir özelliğini (ör. özel bir madde işareti aralığındaki madde işareti rengi) değiştirmeniz gerekiyorsa kapsama oluşturulurken referansla birlikte setText() çağrısının birden fazla kez yapılmasını önleyebilirsiniz. Kapsamı değiştirmeniz gerektiğinde, referansı değiştirebilir ve ardından değiştirdiğiniz özelliğin türüne bağlı olarak TextView üzerinden invalidate() veya requestLayout() yöntemini ç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 bir düğmeye dokunulduğunda gri renge dönüşü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 dokümanlarına bakın.