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

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

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

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.

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

\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
Ö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.