Bir TextView içinde birden fazla stil olması mümkün müdür?


547

Bir TextView içindeki farklı metin parçaları için birden fazla stil ayarlamak mümkün müdür?

Örneğin, metni şu şekilde ayarlıyorum:

tv.setText(line1 + "\n" + line2 + "\n" + word1 + "\t" + word2 + "\t" + word3);

Her metin öğesi için farklı bir stile sahip olmak mümkün müdür? Örneğin, satır1 kalın, sözcük1 italik, vb.

Geliştirici kılavuzunun Android'de Ortak Görevler ve Nasıl Yapılır Metninin Bölümlerini Seçme, Vurgulama veya Stil Oluşturma şunları içerir :

// Get our EditText object.
EditText vw = (EditText)findViewById(R.id.text);

// Set the EditText's text.
vw.setText("Italic, highlighted, bold.");

// If this were just a TextView, we could do:
// vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE);
// to force it to use Spannable storage so styles can be attached.
// Or we could specify that in the XML.

// Get the EditText's internal text storage
Spannable str = vw.getText();

// Create our span sections, and assign a format to each.
str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Ancak bu, metnin içinde açık konum numaraları kullanır. Bunu yapmanın daha temiz bir yolu var mı?


6
TextView dizesi statikse, dizeler kaynak dosyasına html <b>, <i> ve <u> etiketleri ekleyebilir ve bunlar otomatik olarak uygulanır. Örneğin, <stringView android: text = "@ string / test" /> burada @ string / test <string> <b> kalın </b>, <i>italic</i> </string> olarak
ayarlandı

2
+1 @ greg7gkb! Anahtar kelime 'statik'tir. Bazı tellerimin neden <b> ile çalıştığını ve bazılarının çalışmadığını merak ederek saçımı çekiyordum. İçlerinde değişken olmayanlar.
Scott Biggs

Yanıtlar:


694

Durumda, herkes bunun nasıl yapılacağını merak ediyor, bir yol var: (Mark'a tekrar teşekkürler!)

mBox = new TextView(context);
mBox.setText(Html.fromHtml("<b>" + title + "</b>" +  "<br />" + 
            "<small>" + description + "</small>" + "<br />" + 
            "<small>" + DateAdded + "</small>"));

Bu yöntem tarafından desteklenen resmi olmayan bir etiket listesi için bu bağlantıya veya şu soruya bakın: Android TextView tarafından hangi HTML etiketleri destekleniyor?


1
@ JānisGruzis: Bunu yapmanın bir yolu belki de, bir saplama yöntemiyle, diyelim geçer formatTextWhite(string text)sadece aşağıdaki biçim dizesi metin ekler o: "<font size="..." color="..." face="...">%s</font>".
Efsane

@Legend <font fgcolor = '# ffff5400'> <b> <big> "+" Title "+" </big> </b> </font> kullandım, ancak fgcolor / color (ikisi de denendi) değil çalışma ... html kullanarak renk şeyi nasıl yapacağınızı biliyor musunuz
Muhammad Babar

@MuhammadBabar: Bunu deneyebilir misiniz Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");? Bunun benim için bir ara işe yaradığını hatırlıyorum. Hala çalışıp çalışmadığından emin değilim.
Efsane

1
@ Teşekkürler ama etrafında bir yol var bir Html.fromHtml("<font color=\"#999999\">kaçış dizileri benim için çalıştı :)
Muhammed Babar

2
Bu neden mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);herhangi bir fikir işe yaramıyor
Shan Xeeshi

216

Deneyin Html.fromHtml()ve kalın ile metni işaretlemeniz ve italik HTML etiketleri örn:

Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);

1
Aslında bu beni başka bir soruya götürür: İçinde html metni olan bir metin görünümüne veya html sınıfını kullanmadan farklı biçimlendirme kurulumuna sahip üç metin görünümüne sahip olmak daha mı iyi? Açıkçası ilk olduğunu varsayıyorum ama sadece onaylamak istedim ...
Legend

2
Html.fromHtml () desteklediği tam liste listesi hakkında hiçbir fikrim yok - kaynak koduna bakmak gerekir. Satır içi işaretleme ve birden çok TextView widget'ı dikey kararlar olmalıdır. Ayrık metin parçalarının kesin olarak yerleştirilmesi gerekiyorsa, birden çok widget kullanın. Bir widget'ta işaretleme satır içi satır işaretine ihtiyacınız varsa satır içi işaretlemeyi kullanın. Unutmayın: Android'de FlowLayout yoktur, bu nedenle bir paragraf oluşturmak için birden fazla TextView'i bir araya getirmek gerçekten pratik AFAIK değildir.
CommonsWare

1
Bunun için teşekkürler ... Aslında, <small> etiketi işe yaradı ... Bu yüzden basit tutacağım ve kullanacağım ...
Legend

1
@TimBoland: Açıklıklarınızı atıyorsunuz monthText + " " + month.getYearLabel(). Bkz StringUtils.concat().
CommonsWare

2
@TimBoland: Bunların hepsini bir arada yapabilirsiniz: monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))eğer tek tek parçalara ihtiyacınız yoksa.
CommonsWare

191

Biraz konu dışı, ama bunu burada belirtilemeyecek kadar kullanışlı buldum.

Peki, string.xml kaynağından Html metnini okumak ve böylece yerelleştirmeyi kolaylaştırmak istiyorsak. CDATA bunu mümkün kılar:

<string name="my_text">
  <![CDATA[
    <b>Autor:</b> Mr Nice Guy<br/>
    <b>Contact:</b> myemail@grail.com<br/>
    <i>Copyright © 2011-2012 Intergalactic Spacebar Confederation </i>
  ]]>
</string> 

Java kodumuzdan şu şekilde kullanabiliriz:

TextView tv = (TextView) findViewById(R.id.myTextView);
tv.setText(Html.fromHtml(getString(R.string.my_text))); 

Bunun çalışmasını beklemiyordum. Ama oldu.

Umarım bazılarınız için faydalıdır!


2
Genellikle "false" ifadesi, arama tablosunda garip bir değere eriştiğiniz anlamına gelir. Bunu R.id.ok ve R.string.ok vardı ve yanlışlıkla doğru getString (R.string.ok) yerine getString (R.id.ok) kullandığımda aldım
Joe Plante

Bu bir düzen XML biçiminde de çalışıyor mu? Ben orada dize kaynağına "@string/someText"(burada "someText" bir strings.xml tanımlı bir kaynaktır) başvurduğumda, sadece tüm HTML etiketleri ile dizeyi 'metin' olarak alıyorum.
Gee.E

3
Bunu en iyi cevap buldum. Bana ilham verdi. Yardım edemedim ama benim iki sent de cevap değer. Buradaki çözüm gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 , düzen dosyası olarak atamanızı sağlar, böylece metin kaynağını programlı olarak atamanıza gerek kalmaz (ve Android Studio'da önizleme yapabilirsiniz!)
Jon

127

Html kullanmak istemiyorsanız, bir styles.xml dosyası oluşturabilir ve aşağıdaki gibi kullanabilirsiniz:

TextView tv = (TextView) findViewById(R.id.textview);
SpannableString text = new SpannableString(myString);

text.setSpan(new TextAppearanceSpan(getContext(), R.style.myStyle), 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new TextAppearanceSpan(getContext(), R.style.myNextStyle), 6, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

tv.setText(text, TextView.BufferType.SPANNABLE);

üzerinde çalışabileceğiniz basit bir android uygulamasına kopyalayın / yapıştırın. Tek bir metin görünümünde iki stilin uygulanacağını göreceksiniz.
Kent Andersen

üzgünüm net değildim. stilin nereye uygulanacağının alt dizesini (iki dizin aracılığıyla) belirtmeniz gerekiyorsa, dizinler elbette her yerel ayar için farklı olacağından yerelleştirilmiş dizeler için çalışmaz.
Jeffrey Blattman

Haklısın. Birden fazla dili destekliyorsanız, bu gitmek istediğiniz rota olmaz. Dizenin boyutları değiştirmeyeceğinden emin değilseniz ... örneğin uygulama adı gibi.
Kent Andersen

9
L10n'ın neden bu çözümle çalışamadığını anlamıyorum. Sadece bir tane yerine ayrı dizeler alın: String firstPart = getString (R.string.line1); String secondPart = getString (R.string.line2); Yayılabilir metin = yeni SpannableString (firstPart + secondPart); text.setSpan (yeni ForegroundColorSpan (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (metin, TextView.BufferType.SPANNABLE);
Rui

5
@ Rui, çünkü l10n konumsaldır. kelimelerin konumunu bir cümleyle kodlayamazsınız ve hala l10n olamazsınız. işte bu yüzden $1%s is a happy $2%sstring demetlerinde olanları görüyorsunuz . belirteçlerin yeniden düzenlenmesine izin vermelisiniz.
Jeffrey Blattman

60

SpannableStringHtml işaretlemesi yerine kullanmak daha hafiftir . Görsel örnekleri görmeme yardımcı oluyor, bu yüzden burada ek bir cevap var.

resim açıklamasını buraya girin

Bu bir single TextView.

// set the text
SpannableString s1 = new SpannableString("bold\n");
SpannableString s2 = new SpannableString("italic\n");
SpannableString s3 = new SpannableString("foreground color\n");
SpannableString s4 = new SpannableString("background color\n");
SpannableString s5 = new SpannableString("underline\n");
SpannableString s6 = new SpannableString("strikethrough\n");
SpannableString s7 = new SpannableString("bigger\n");
SpannableString s8 = new SpannableString("smaller\n");
SpannableString s9 = new SpannableString("font\n");
SpannableString s10 = new SpannableString("URL span\n");
SpannableString s11 = new SpannableString("clickable span\n");
SpannableString s12 = new SpannableString("overlapping spans\n");

// set the style
int flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
s1.setSpan(new StyleSpan(Typeface.BOLD), 0, s1.length(), flag);
s2.setSpan(new StyleSpan(Typeface.ITALIC), 0, s2.length(), flag);
s3.setSpan(new ForegroundColorSpan(Color.RED), 0, s3.length(), flag);
s4.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, s4.length(), flag);
s5.setSpan(new UnderlineSpan(), 0, s5.length(), flag);
s6.setSpan(new StrikethroughSpan(), 0, s6.length(), flag);
s7.setSpan(new RelativeSizeSpan(2), 0, s7.length(), flag);
s8.setSpan(new RelativeSizeSpan(0.5f), 0, s8.length(), flag);
s9.setSpan(new TypefaceSpan("monospace"), 0, s9.length(), flag);
s10.setSpan(new URLSpan("https://developer.android.com"), 0, s10.length(), flag);
s11.setSpan(new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        Toast.makeText(getApplicationContext(), "Span clicked", Toast.LENGTH_SHORT).show();
    }
}, 0, s11.length(), flag);
s12.setSpan(new ForegroundColorSpan(Color.RED), 0, 11, flag);
s12.setSpan(new BackgroundColorSpan(Color.YELLOW), 4, s12.length(), flag);
s12.setSpan(new UnderlineSpan(), 4, 11, flag);

// build the string
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(s1);
builder.append(s2);
builder.append(s3);
builder.append(s4);
builder.append(s5);
builder.append(s6);
builder.append(s7);
builder.append(s8);
builder.append(s9);
builder.append(s10);
builder.append(s11);
builder.append(s12);

// set the text view with the styled text
textView.setText(builder);
// enables clicking on spans for clickable span and url span
textView.setMovementMethod(LinkMovementMethod.getInstance());

İlerideki çalışma

Bu örnek başlangıçta buradan ilham almıştır .


5
Yayınınız SpannableString ile ilgili Google Dokümantasyonundan daha iyi .. Teşekkürler
MBH

Bu, web hizmetinden gelen resimleri destekledi mi. Benim ihtiyacım böyle.
MohanRaj S

@MohanRajS SpannableStrings Unicode emojisini destekler, ancak diğer görüntüleri desteklemez.
19'da Suragch

@Suragch hızlı yanıt için teşekkür ederim, ama benim yan gereksinim jpg, png. Sadece tek seçenek web görünümüdür? :(
MohanRaj S

43

Desteklenen etiketlerin listesi:

Bir dize kaynağı kullanıyorsanız, HTML gösterimini kullanarak kalın veya italik gibi bazı basit stiller ekleyebilirsiniz. Şu anda desteklenen etiketler şunlardır: B(kalın), I(italik), U(alt çizgi), TT(sabit aralıklı), BIG, SMALL, SUP(üst simge), SUB(simge) ve STRIKE(çizili). Yani, örneğin, res/values/strings.xmlbunu beyan edebilirsiniz:

<resource>
    <string id="@+id/styled_welcome_message">We are <b><i>so</i></b> glad to see you.</string>
</resources>

( Http://developer.android.com/guide/faq/commontasks.html#selectingtext - Web Arşivi bağlantısından <resource>yazım hatası orijinaldir!)

Ayrıca Html.fromHtmlbasit durumlarda gerçekten gerekli olmadığını da gösterir .


Html.fromHtml genellikle metin biçimlendirmenin daha kolay bir yoludur. Ayrıca, bu bağlantı zaten orijinal soruda.
Intrications

17

Ben de aynı problemle karşılaşıyordum. FromHtml kullanabilirsiniz, ama şimdi android değil, web değil, bu yüzden bunu denemeye karar verdi. Ben dize değiştirme kavramı kullanarak bir atış verdi bu yüzden olsa yerelleştirmek zorunda. TextView'daki stili ana stil olarak ayarladım, sonra sadece diğer peices formatlayın.

Umarım bu aynı şeyi yapmak isteyen başkalarına yardımcı olur - bunun çerçevede neden daha kolay olmadığını bilmiyorum.

Dizelerim şöyle:


<string name="my_text">{0} You will need a {1} to complete this assembly</string>
<string name="text_sub0">1:</string>
<string name="text_sub1">screwdriver, hammer, and measuring tape</string>

İşte stiller:


<style name="MainStyle">
    <item name="android:textSize">@dimen/regular_text</item>
    <item name="android:textColor">@color/regular_text</item>
</style>
<style name="style0">
    <item name="android:textSize">@dimen/paragraph_bullet</item>
    <item name="android:textColor">@color/standout_text</item>
    <item name="android:textStyle">bold</item>
</style>
<style name="style1">
    <item name="android:textColor">@color/standout_light_text</item>
    <item name="android:textStyle">italic</item>
</style>

Benim formatStyles yöntemini çağıran benim kod İşte:


SpannableString formattedSpan = formatStyles(getString(R.string.my_text), getString(R.string.text_sub0), R.style.style0, getString(R.string.main_text_sub1), R.style.style1);
textView.setText(formattedSpan, TextView.BufferType.SPANNABLE);

Biçim yöntemi:


private SpannableString formatStyles(String value, String sub0, int style0, String sub1, int style1)
{
    String tag0 = "{0}";
    int startLocation0 = value.indexOf(tag0);
    value = value.replace(tag0, sub0);

    String tag1 = "{1}";
    int startLocation1 = value.indexOf(tag1);
    if (sub1 != null && !sub1.equals(""))
    {
        value = value.replace(tag1, sub1);
    }

    SpannableString styledText = new SpannableString(value);
    styledText.setSpan(new TextAppearanceSpan(getActivity(), style0), startLocation0, startLocation0 + sub0.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    if (sub1 != null && !sub1.equals(""))
    {
        styledText.setSpan(new TextAppearanceSpan(getActivity(), style1), startLocation1, startLocation1 + sub1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    return styledText;
}

Dizenin yerelleştirilmiş bir sürümü {0} ve {1} yeniden sıralarsa bu işe yarayacağını sanmıyorum. StartLocation1 <startLocation0
JonathanC


8

HTMLBuilder kullanarak bunu yapmanın kolay bir yolu

    myTextView.setText(new HtmlBuilder().
                    open(HtmlBuilder.Type.BOLD).
                    append("Some bold text ").
                    close(HtmlBuilder.Type.BOLD).
                    open(HtmlBuilder.Type.ITALIC).
                    append("Some italic text").
                    close(HtmlBuilder.Type.ITALIC).
                    build()
    );

Sonuç:

Bazı kalın metinler Bazı italik metinler


7

Biçimlendirilmiş metni xml'ye ekleyebilmek istiyorsanız, TextView'i genişleten ve setText () öğesini geçersiz kılan özel bir görünüm oluşturabilirsiniz:

public class HTMLStyledTextView extends TextView
{
    public HTMLStyledTextView(Context context) {
        super(context);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setText(CharSequence text, BufferType type)
    {
       super.setText(Html.fromHtml(text.toString()), type);
    }
}

Ardından, şu şekilde kullanabilirsiniz ( PACKAGE_NAMEpaket adınızla değiştirin ):

<PACKAGE_NAME.HTMLStyledTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="<![CDATA[
        <b>Bolded Text:</b> Non-Bolded Text
    ]]>"
/>

Android Studio'da önizleme düzgün yapılıyor mu?
JPM


3

Ben de

Kotlin ve Anko ile güzel bir işaretleme kullanmaya ne dersiniz -

import org.jetbrains.anko.*
override fun onCreate(savedInstanceState: Bundle?) {
    title = "Created with Beautiful Markup"
    super.onCreate(savedInstanceState)

    verticalLayout {
        editText {
            hint = buildSpanned {
                append("Italic, ", Italic)
                append("highlighted", backgroundColor(0xFFFFFF00.toInt()))
                append(", Bold", Bold)
            }
        }
    }
}

Güzel İşaretleme ile oluşturuldu


2

Evet, kullanmak mümkündür SpannedString. Kotlin kullanıyorsanız, bunu yapmak core-ktxiçin alana özel bir dil (DSL) sağladığı için kullanmak daha da kolaylaşır :

    val string: SpannedString = buildSpannedString {
        bold {
            append("1111")
        }
        append("Devansh")     
    }

Sağladığı diğer seçenekler:

append("Hello There")
bold {
    append("bold")
    italic {
        append("bold and italic")
        underline {
            append("then some text with underline")
        }
    }
}

Sonunda şunları yapabilirsiniz:

textView.text = string

0

Aslında, Html nesnesi dışında, Spannable türü sınıflarını da kullanabilirsiniz, örneğin TextAppearanceSpan veya TypefaceSpan ve SpannableString togather. Html sınıfı da bu mekanizmaları kullanır. Ancak Spannable tipi sınıflarla daha fazla özgürlüğe sahip olursunuz.


0

String'in length () yöntemini kullanmak kadar basit olabilir:

  1. Dizeler XML dosyasındaki metin dizesini, farklı stillere ihtiyacınız olduğu kadar alt dizeye (Android'in bakış açısından ayrı bir dizeler) ayırın, böylece şöyle olabilir: str1, str2, str3 (durumunuzda olduğu gibi), birlikte katıldığında kullandığınız tüm tek dize vardır.

  2. Daha sonra, tıpkı kodunuzda sunduğunuz gibi "Span" yöntemini uygulayın - ancak tek bir dize yerine, bunları birleştiren tüm alt dizeleri, her biri farklı bir özel stile sahip tek bir dizede birleştirin.

Yine de sayıları kullanıyorsunuz, ancak doğrudan değil - artık kodlanmış bir form almıyorlar (kodunuzda olduğu gibi), ancak birleşik length () yöntemleriyle değiştiriliyorlar (str'den önceki ve soneki iki yıldız not edin). Değişikliği yok etmek için mutlak sayı yerine length ()):

str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, **str.length()**, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

ilk dize boyutu için, sonra ikinci dize boyutu için str.length () + 1, str.length () + str2.length () vb., örneğin 0,7 veya 8,19 yerine tüm alt dizelerle ve bunun gibi...


0

Android String Kaynakları olarak yardımcı Spannable Sınıfının kullanılması web sayfasının alt kısmında paylaşılır. Buna oluşturarak yaklaşabilirsiniz.CharSquencesOnlara bir stil ve onlara bir stil vererek .

Ancak bize verdikleri örnekte, sadece kalın, italik ve hatta metni renklendirmek içindir. CharSequenceOnları a ayarlamak için bir kaç stilleri sarmak gerekiyordu TextView. Bu sınıfa (adını CharSequenceStylesverdim) bu fonksiyonu ekledim.

public static CharSequence applyGroup(LinkedList<CharSequence> content){
    SpannableStringBuilder text = new SpannableStringBuilder();
    for (CharSequence item : content) {
        text.append(item);
    }
    return text;
}

Ve görünümde bunu ekledim.

            message.push(postMessageText);
            message.push(limitDebtAmount);
            message.push(pretMessageText);
            TextView.setText(CharSequenceStyles.applyGroup(message));

Umarım bu sana yardımcı olur!


0

Spanny SpannableString'in kullanımını kolaylaştırır.

Spanny spanny = new Spanny("Underline text", new UnderlineSpan())
                .append("\nRed text", new ForegroundColorSpan(Color.RED))
                .append("\nPlain text");
textView.setText(spanny)

0

Jon'un dediği gibi , benim için bu en iyi çözüm ve çalışma zamanında herhangi bir metin ayarlamanıza gerek yok, sadece bu özel sınıfı HtmlTextView kullanın

public class HtmlTextView extends TextView {

  public HtmlTextView(Context context) {
      super(context);
  }

  public HtmlTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr) 
  {
      super(context, attrs, defStyleAttr);
  }

  @TargetApi(21)
  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public void setText(CharSequence s,BufferType b){
      super.setText(Html.fromHtml(s.toString()),b);
  }

}

ve bu, şimdi sadece XML'inize koyun

<com.fitc.views.HtmlTextView
    android:id="@+id/html_TV"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/example_html" />

Html Dizenizle

<string name="example_html">
<![CDATA[
<b>Author:</b> Mr Donuthead<br/>
<b>Contact:</b> me@donut.com<br/>
<i>Donuts for life </i>
]]>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.