Apakah mungkin memiliki beberapa gaya di dalam TextView?


547

Apakah mungkin untuk mengatur beberapa gaya untuk potongan teks yang berbeda di dalam TextView?

Misalnya, saya mengatur teks sebagai berikut:

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

Apakah mungkin untuk memiliki gaya yang berbeda untuk setiap elemen teks? Misal, line1 bold, word1 italic, dll.

Tugas Umum panduan pengembang dan Cara Melakukannya di Android termasuk Memilih, Menyorot, atau Styling Bagian Teks :

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

Tapi itu menggunakan nomor posisi eksplisit di dalam teks. Apakah ada cara yang lebih bersih untuk melakukan ini?


6
Jika string TextView statis, Anda bisa menambahkan tag html <b>, <i>, dan <u> ke file sumber daya string dan mereka akan diterapkan secara otomatis. Misalnya <TextView android: text = "@ string / test" /> di mana @ string / test diatur ke <string> <b> bold </b>, <i>italic</i> </string>
greg7gkb

2
+1 @ greg7gkb! Kata kuncinya adalah 'statis'. Saya mencabut rambut saya bertanya-tanya mengapa beberapa string saya bekerja dengan <b> dan beberapa tidak. Yang tidak memiliki variabel di dalamnya.
Scott Biggs

Jawaban:


694

Dalam hal, ada yang bertanya-tanya bagaimana melakukan ini, ini salah satu caranya: (Terima kasih kepada Mark lagi!)

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

Untuk daftar tag tidak resmi yang didukung oleh metode ini, lihat tautan ini atau pertanyaan ini: Tag HTML mana yang didukung oleh Android TextView?


1
@ JānisGruzis: Mungkin salah satu cara untuk melakukan ini adalah melalui metode stub, katakanlah, formatTextWhite(string text)bahwa hanya menyisipkan teks ke dalam format string berikut: "<font size="..." color="..." face="...">%s</font>".
Legenda

@ Legenda saya telah menggunakan <font fgcolor = '# ffff5400'> <b> <big> "+" Judul "+" </big> </b> "tetapi fgcolor / warna (keduanya dicoba) tidak bekerja ... apakah Anda tahu bagaimana melakukan hal warna menggunakan html
Muhammad Babar

@MuhammadBabar: Dapatkah Anda mencoba ini: Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");? Saya ingat ini kembali bekerja untuk saya. Tidak yakin apakah masih berfungsi.
Legenda

1
@Legend terima kasih tetapi ada jalan keluarnya Html.fromHtml("<font color=\"#999999\">urutan pelarian bekerja untuk saya :) ...
Muhammad Babar

2
mengapa ini tidak berhasil, mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);terima kasih
Shan Xeeshi

216

Coba Html.fromHtml(), dan tandai teks Anda dengan tag HTML tebal dan miring misalnya:

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

1
Sebenarnya itu membuat saya ke pertanyaan lain: Apakah lebih baik memiliki satu tampilan teks dengan teks html di dalamnya atau tiga tampilan teks dengan pengaturan markup yang berbeda tanpa menggunakan kelas html? Saya berasumsi ini jelas yang pertama tetapi hanya ingin mengkonfirmasi ...
Legenda

2
Saya tidak tahu apa daftar lengkap tag yang didukung oleh Html.fromHtml () - Anda perlu melihat kode sumbernya. Markup sebaris dan beberapa widget TextView harus menjadi keputusan ortogonal. Gunakan banyak widget jika Anda membutuhkan penempatan tepat bit teks. Gunakan markup inline jika Anda, um, perlu markup inline di widget. Ingat: tidak ada FlowLayout di Android, jadi merangkai bersama beberapa TextViews untuk membuat paragraf bukanlah AFAIK yang benar-benar praktis.
CommonsWare

1
Terima kasih untuk itu ... Sebenarnya, tag <small> bekerja ... Jadi saya akan membuatnya tetap sederhana dan hanya menggunakannya ...
Legenda

1
@TimBoland: Anda membuang bentang melalui monthText + " " + month.getYearLabel(). Lihat StringUtils.concat().
CommonsWare

2
@TimBoland: Anda bisa melakukan itu semua dalam satu:, monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))jika Anda tidak membutuhkan masing-masing bagian.
CommonsWare

191

Agak di luar topik, tetapi saya menemukan ini terlalu berguna untuk tidak disebutkan di sini.

Bagaimana jika kita ingin membaca teks Html dari sumber string.xml dan dengan demikian membuatnya mudah untuk dilokalkan. CDATA memungkinkan ini:

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

Dari kode Java kami, kami sekarang dapat menggunakannya seperti ini:

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

Saya tidak berharap ini bekerja. Tapi itu terjadi.

Semoga bermanfaat bagi sebagian dari Anda!


2
Biasanya "false" menyiratkan bahwa Anda mengakses nilai aneh di tabel pencarian. Saya mendapatkan ini ketika saya memiliki R.id.ok dan R.string.ok, dan secara tidak sengaja menggunakan getString (R.id.ok) alih-alih getString (R.string.ok) yang benar
Joe Plante

Apakah ini berfungsi dalam tata letak-XML juga? Ketika saya merujuk ke sumber daya string di sana dengan "@string/someText"(di mana "someText" adalah sumber daya yang didefinisikan dalam strings.xml), saya hanya mendapatkan string dengan semua tag HTML sebagai 'teks'.
Astaga.

3
Saya menemukan ini jawaban terbaik. Menginspirasi saya. Mau tidak mau aku harus memasukkan jawaban dua sen layak juga. Solusi di sini gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 memungkinkan Anda untuk menetapkan dalam file tata letak sehingga Anda tidak perlu menetapkan sumber teks secara terprogram (dan dapat mempratinjau di Android Studio!)
Jon

127

Jika Anda merasa tidak ingin menggunakan html, Anda bisa membuat styles.xml dan menggunakannya seperti ini:

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

salin / rekatkan ke aplikasi android sederhana yang mungkin sedang Anda kerjakan. Anda akan melihat bahwa pada tampilan teks tunggal dua gaya akan diterapkan.
Kent Andersen

maaf saya tidak jelas. jika Anda harus menentukan substring (melalui dua indeks) tempat menerapkan gaya, itu tidak berfungsi untuk string terlokalisasi karena indeks tentu saja akan berbeda untuk setiap lokal.
Jeffrey Blattman

Anda benar. Jika Anda mendukung berbagai bahasa, ini bukan rute yang ingin Anda ambil. Kecuali Anda yakin string tidak akan mengubah ukuran ... seperti nama aplikasi misalnya.
Kent Andersen

9
Saya tidak mengerti mengapa l10n tidak dapat bekerja dengan solusi ini. Hanya dapatkan string terpisah alih-alih hanya satu: String firstPart = getString (R.string.line1); String secondPart = getString (R.string.line2); Teks yang dapat dip Spannable = SpannableString baru (firstPart + secondPart); text.setSpan (ForegroundColorSpan baru (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (teks, TextView.BufferType.SPANNABLE);
Rui

5
@Rui karena l10n juga posisional. Anda tidak dapat mengubah kode posisi kata dalam frasa dan masih l10n. itu sebabnya Anda melihat hal-hal seperti $1%s is a happy $2%sdi bundel string. Anda harus mengizinkan token untuk diatur ulang.
Jeffrey Blattman

60

Lebih ringan untuk menggunakan SpannableStringmarkup html. Ini membantu saya untuk melihat contoh visual jadi inilah jawaban tambahan.

masukkan deskripsi gambar di sini

Ini adalah 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());

Pelajaran lanjutan

Contoh ini awalnya terinspirasi dari sini .


5
Posting Anda lebih baik daripada Dokumentasi Google tentang SpannableString .. Terima kasih
MBH

Apakah ini mendukung Gambar yang dari layanan web. Kebutuhan saya seperti itu.
MohanRaj S

@MohanRajS SpannableStrings mendukung emoji Unicode, tetapi bukan gambar lain.
Suragch

@ Suragch terima kasih atas respons yang cepat, tetapi persyaratan pihak saya adalah jpg, png. Satu-satunya pilihan untuk menangani adalah tampilan web? :(
MohanRaj S

43

Daftar tag yang didukung adalah:

Jika Anda menggunakan sumber daya string, Anda dapat menambahkan beberapa gaya sederhana, seperti tebal atau miring menggunakan notasi HTML. Tag yang saat ini didukung adalah: B(bold), I(miring), U(garis bawah), TT(monospace), BIG, SMALL, SUP(superscript), SUB(subscript), dan STRIKE(strikethrough). Jadi, misalnya, res/values/strings.xmlAnda dapat mendeklarasikan ini:

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

(Dari http://developer.android.com/guide/faq/commontasks.html#selectingtext - tautan Web Archive, <resource>salah ketik itu asli!)

Ini juga menunjukkan bahwa Html.fromHtmlsebenarnya tidak diperlukan dalam kasus-kasus sederhana.


Html.fromHtml sering merupakan cara yang lebih mudah untuk menata teks. Juga, tautan ini sudah ada di pertanyaan awal.
Intrications

17

Saya mengalami masalah yang sama. Saya bisa menggunakan fromHtml, tapi sekarang saya android, bukan web, jadi saya memutuskan untuk mencobanya. Saya harus melokalkan ini jadi saya mencobanya menggunakan konsep penggantian string. Saya mengatur gaya pada TextView menjadi gaya utama, kemudian hanya memformat peices lainnya.

Saya harap ini membantu orang lain yang ingin melakukan hal yang sama - Saya tidak tahu mengapa ini tidak mudah dalam kerangka kerja.

Senar saya terlihat seperti ini:


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

Berikut adalah gaya-gayanya:


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

Ini kode saya yang memanggil metode formatStyles saya:


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

Metode format:


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

Saya tidak berpikir ini akan berfungsi jika versi lokal string menata ulang {0} dan {1}. Anda mungkin harus mengubah startLocation0 jika startLocation1 adalah <startLocation0
JonathanC


8

Berikut adalah cara mudah untuk melakukannya menggunakan HTMLBuilder

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

Hasil:

Beberapa teks tebal Beberapa teks miring


7

Jika Anda ingin dapat menambahkan teks gaya dalam xml Anda dapat membuat tampilan kustom memperluas TextView dan menimpa setText ():

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

Kemudian, Anda dapat menggunakannya seperti ini (ganti PACKAGE_NAMEdengan nama paket Anda):

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

Apakah pratinjau di Android Studio sudah benar?
JPM


3

Saya juga

Bagaimana kalau menggunakan markup indah dengan Kotlin dan Anko -

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

Dibuat dengan Markup Indah


2

Ya, dimungkinkan menggunakan SpannedString. Jika Anda menggunakan Kotlin, itu menjadi lebih mudah dilakukan dengan menggunakan core-ktx, karena menyediakan bahasa khusus domain (DSL) untuk melakukan ini:

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

Lebih banyak opsi yang disediakan olehnya adalah:

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

Akhirnya, Anda bisa langsung ke:

textView.text = string

0

Bahkan, kecuali objek Html, Anda juga bisa menggunakan kelas tipe Spannable, mis. TextAppearanceSpan atau TypefaceSpan dan SpannableString. Kelas html juga menggunakan mekanisme ini. Tetapi dengan kelas tipe Spannable, Anda memiliki lebih banyak kebebasan.


0

Mungkin sesederhana memanfaatkan metode panjang String ():

  1. Pisahkan string teks dalam file XML Strings menjadi banyak sub-string (string terpisah dari sudut pandang Android) sebanyak yang Anda butuhkan gaya yang berbeda, sehingga bisa seperti: str1, str2, str3 (seperti dalam kasus Anda), yang ketika digabungkan bersama adalah seluruh string tunggal yang Anda gunakan.

  2. Dan kemudian cukup ikuti metode "Rentang", sama seperti Anda disajikan dengan kode Anda - tetapi alih-alih satu string, gabungkan semua substring yang menggabungkannya menjadi satu, masing-masing dengan gaya kustom yang berbeda.

Anda masih menggunakan angka-angka, namun tidak secara langsung - mereka tidak lagi mengambil bentuk hardcode (seperti dalam kode Anda) sekarang, tetapi mereka diganti dengan metode panjang gabungan () (perhatikan dua bintang sebelum dan akhiran str. length () sebagai pengganti angka absolut untuk menghilangkan perubahan):

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

untuk ukuran string pertama, kemudian str.length () + 1, str.length () + str2.length () untuk ukuran string kedua, dan seterusnya dengan semua substring, bukan misalnya 0,7 atau 8,19 dan seterusnya...


0

Menggunakan Kelas Spannable tambahan sebagai Android String Resources berbagi di bagian bawah halaman web. Anda dapat mendekati ini dengan membuat CharSquencesdan memberi mereka gaya.

Tetapi dalam contoh yang mereka berikan kepada kami, hanya untuk teks tebal, miring, dan bahkan berwarna. Saya perlu membungkus beberapa gaya dalam CharSequenceuntuk mengaturnya dalam TextView. Jadi untuk Kelas itu (saya menamainya CharSequenceStyles) saya baru saja menambahkan fungsi ini.

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

Dan dalam pandangan saya menambahkan ini.

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

Saya harap ini membantu Anda!


0

Spanny membuat SpannableString lebih mudah digunakan.

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

0

Seperti yang dikatakan Jon , bagi saya ini adalah solusi terbaik dan Anda tidak perlu mengatur teks saat runtime, hanya gunakan kelas khusus ini HtmlTextView

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

}

dan itu saja, sekarang hanya taruh di XML Anda

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

dengan String Html Anda

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.