深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格

背景介绍
在开发应用过程中经常会遇到显示一些不同的字体风格的信息犹如默认的LockScreen上面的时间和充电信息。对于类似的情况,可能第一反应就是用不同的多个TextView来实现,对于每个TextView设置不同的字体风格以满足需求。

这里推荐的做法是使用android.text.*;和android.text.style.*;下面的组件来实现RichText:也即在同一个TextView中设置不同的字体风格。对于某些应用,比如文本编辑,记事本,彩信,短信等地方,还必须使用这些组件才能达到想到的显示效果。

主要的基本工具类有android.text.Spanned; android.text.SpannableString; android.text.SpannableStringBuilder;使用这些类来代替常规String。SpannableString和SpannableStringBuilder可以用来设置不同的Span,这些Span便是用于实现Rich Text,比如粗体,斜体,前景色,背景色,字体大小,字体风格等等,android.text.style.*中定义了很多的Span类型可供使用。

这是相关的API的Class General Hierarchy:

因为Spannable等最终都实现了CharSequence接口,所以可以直接把SpannableString和SpannableStringBuilder通过TextView.setText()设置给TextView。
使用方法
当要显示Rich Text信息的时候,可以使用创建一个SpannableString或SpannableStringBuilder,它们的区别在于SpannableString像一个String一样,构造对象的时候传入一个String,之后再无法更改String的内容,也无法拼接多个SpannableString;而SpannableStringBuilder则更像是StringBuilder,它可以通过其append()方法来拼接多个String:


代码如下:

SpannableString word = new SpannableString("The quick fox jumps over the lazy dog");
SpannableStringBuilder multiWord = new SpannableStringBuilder();
multiWord.append("The Quick Fox");
multiWord.append("jumps over");
multiWord.append("the lazy dog");

创建完Spannable对象后,就可以为它们设置Span来实现想要的Rich Text了,常见的Span有:
•AbsoluteSizeSpan(int size) ---- 设置字体大小,参数是绝对数值,相当于Word中的字体大小
•RelativeSizeSpan(float proportion) ---- 设置字体大小,参数是相对于默认字体大小的倍数,比如默认字体大小是x, 那么设置后的字体大小就是x*proportion,这个用起来比较灵活,proportion>1就是放大(zoom in), proportion<1就是缩小(zoom out)
•ScaleXSpan(float proportion) ---- 缩放字体,与上面的类似,默认为1,设置后就是原来的乘以proportion,大于1时放大(zoon in),小于时缩小(zoom out)
•BackgroundColorSpan(int color) ----背景着色,参数是颜色数值,可以直接使用android.graphics.Color里面定义的常量,或是用Color.rgb(int, int, int)
•ForegroundColorSpan(int color) ----前景着色,也就是字的着色,参数与背景着色一致
•TypefaceSpan(String family) ----字体,参数是字体的名字比如“sans", "sans-serif"等
•StyleSpan(Typeface style) -----字体风格,比如粗体,斜体,参数是android.graphics.Typeface里面定义的常量,如Typeface.BOLD,Typeface.ITALIC等等。
•StrikethroughSpan----如果设置了此风格,会有一条线从中间穿过所有的字,就像被划掉一样
对于这些Sytle span在使用的时候通常只传上面所说明的构造参数即可,不需要设置其他的属性,如果需要的话,也可以对它们设置其他的属性,详情可以参见<文档>。
SpannableString和SpannableStringBuilder都有一个设置上述Span的方法:


代码如下:

/**
 * Set the style span to Spannable, such as SpannableString or SpannableStringBuilder
 * @param what --- the style span, such as StyleSpan
 * @param start --- the starting index of characters to which the style span to apply
 * @param end --- the ending index of characters to which the style span to apply
 * @param flags --- the flag specified to control
 */
setSpan(Object what, int start, int end, int flags);

其中参数what是要设置的Style span,start和end则是标识String中Span的起始位置,而 flags是用于控制行为的,通常设置为0或Spanned中定义的常量,常用的有:
•Spanned.SPAN_EXCLUSIVE_EXCLUSIVE --- 不包含两端start和end所在的端点
•Spanned.SPAN_EXCLUSIVE_INCLUSIVE --- 不包含端start,但包含end所在的端点
•Spanned.SPAN_INCLUSIVE_EXCLUSIVE --- 包含两端start,但不包含end所在的端点
•Spanned.SPAN_INCLUSIVE_INCLUSIVE--- 包含两端start和end所在的端点
这里理解起来就好像数学中定义区间,开区间还是闭区间一样的。还有许多其他的Flag,可以参考<这里>。这里要重点说明下关于参数0,有很多时候,如果设置了上述的参数,那么Span会从start应用到Text结尾,而不是在start和end二者之间,这个时候就需要使用Flag 0。
Linkify
另外,也可以对通过TextView.setAutoLink(int)设置其Linkify属性,其用处在于,TextView会自动检查其内容,会识别出phone number, web address or email address,并标识为超链接,可点击,点击后便跳转到相应的应用,如Dialer,Browser或Email。Linkify有几个常用选项,更多的请参考<文档>
•Linkify.EMAIL_ADDRESS -- 仅识别出TextView中的Email在址,标识为超链接,点击后会跳到Email,发送邮件给此地址
•Linkify.PHONE_NUMBERS -- 仅识别出TextView中的电话号码,标识为超链接,点击后会跳到Dialer,Call这个号码
•Linkify.WEB_URLS-- 仅识别出TextView中的网址,标识为超链接,点击后会跳到Browser打开此URL
•Linkify.ALL -- 这个选项是识别出所有系统所支持的特殊Uri,然后做相应的操作
权衡选择
个人认为软件开发中最常见的问题不是某个技巧怎么使用的问题,而是何时该使用何技巧的问题,因为实现同一个目标可能有N种不同的方法,就要权衡利弊,选择最合适的一个,正如常言所云,没有最好的,只有最适合的。如前面所讨论的,要想用不同的字体展现不同的信息可能的解法,除了用Style Span外还可以用多个TextView。那么就需要总结下什么时候该使用StyleSpan,什么时候该使用多个TextView:
1.如果显示的是多个不同类别的信息,就应该使用多个TextView,这样也方便控制和改变各自的信息,例子就是默认LockScreen上面的日期和充电信息,因为它们所承载不同的信息,所以应该使用多个TextView来分别呈现。
2.如果显示的是同一类信息,或者同一个信息,那么应该使用StyleSpan。比如,短信息中,要把联系人的相关信息突出显示;或是想要Highlight某些信息等。
3.如果要实现Rich text,没办法,只能使用Style span。
4.如果要实现某些特效,也可以考虑使用StyleSpan。设置不同的字体风格只是Style span的初级应用,如果深入研究,可以发现很多奇妙的功效。
实例


代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:orientation="vertical">
  <ScrollView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
     <TextView
       android:id="@+id/text_view_font_1"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
     <TextView
       android:id="@+id/text_view_font_2"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
     <TextView
       android:id="@+id/text_view_font_3"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
     <TextView
       android:id="@+id/text_view_font_4"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
     <TextView
       android:id="@+id/text_view_font_5"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       />
    </LinearLayout>
    </ScrollView>
</LinearLayout>

Source code:


代码如下:

package com.android.effective;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.QuoteSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.ScaleXSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.widget.TextView;
public class TextViewFontActivity extends Activity {
    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.textview_font_1);

// Demonstration of basic SpannableString and spans usage
        final TextView textWithString = (TextView) findViewById(R.id.text_view_font_1);
        String w = "The quick fox jumps over the lazy dog";
        int start = w.indexOf('q');
        int end = w.indexOf('k') + 1;
        Spannable word = new SpannableString(w);
        word.setSpan(new AbsoluteSizeSpan(22), start, end,
                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word.setSpan(new StyleSpan(Typeface.BOLD), start, end,
                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word.setSpan(new BackgroundColorSpan(Color.RED), start, end,
                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        textWithString.setText(word);

// Demonstration of basic SpannableStringBuilder and spans usage
        final TextView textWithBuilder = (TextView) findViewById(R.id.text_view_font_2);
        SpannableStringBuilder word2 = new SpannableStringBuilder();
        final String one = "Freedom is nothing but a chance to be better!";
        final String two = "The quick fox jumps over the lazy dog!";
        final String three = "The tree of liberty must be refreshed from time to time with " +
                "the blood of patroits and tyrants!";
        word2.append(one);
        start = 0;
        end = one.length();
        word2.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        word2.append(two);
        start = end;
        end += two.length();
        word2.setSpan(new ForegroundColorSpan(Color.CYAN), start, end,
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        word2.append(three);
        start = end;
        end += three.length();
        word2.setSpan(new URLSpan(three), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        textWithBuilder.setText(word2);

// Troubleshooting when using SpannableStringBuilder
        final TextView textTroubles = (TextView) findViewById(R.id.text_view_font_3);
        SpannableStringBuilder word3 = new SpannableStringBuilder();
        start = 0;
        end = one.length();
        // Caution: must first append or set text to SpannableStringBuilder or SpannableString
        // then set the spans to them, otherwise, IndexOutOfBoundException is thrown when setting spans
        word3.append(one);
        // For AbsoluteSizeSpan, the flag must be set to 0, otherwise, it will apply this span to until end of text
        word3.setSpan(new AbsoluteSizeSpan(22), start, end, 0);//Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        // For BackgroundColorSpanSpan, the flag must be set to 0, otherwise, it will apply this span to end of text
        word3.setSpan(new BackgroundColorSpan(Color.DKGRAY), start, end, 0); //Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.append(two);
        start = end;
        end += two.length();
        word3.setSpan(new TypefaceSpan("sans-serif"), start, end,
                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        // TODO: sometimes, flag must be set to 0, otherwise it will apply the span to until end of text
        // which MIGHT has nothing to do with specific span type.
        word3.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, 0);//Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.setSpan(new ScaleXSpan(0.618f), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.setSpan(new StrikethroughSpan(), start, end, 0);//Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.setSpan(new ForegroundColorSpan(Color.CYAN), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.setSpan(new QuoteSpan(), start, end, 0); //Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.append(three);
        start = end;
        end += three.length();
        word3.setSpan(new RelativeSizeSpan((float) Math.E), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        word3.setSpan(new ForegroundColorSpan(Color.BLUE), start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        textTroubles.setText(word3);

// Highlight some patterns
        final String four = "The gap between the best software engineering " +
                "practice and the average practice is very wide¡ªperhaps wider " +
                " than in any other engineering discipline. A tool that disseminates " +
                "good practice would be important.¡ªFred Brooks";
        final Pattern highlight = Pattern.compile("the");
        final TextView textHighlight = (TextView) findViewById(R.id.text_view_font_4);
        SpannableString word4 = new SpannableString(four);
        Matcher m = highlight.matcher(word4.toString());
        while (m.find()) {
            word4.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), m.start(), m.end(),
                    Spannable.SPAN_INCLUSIVE_INCLUSIVE);
            word4.setSpan(new ForegroundColorSpan(Color.RED), m.start(), m.end(),
                    Spannable.SPAN_INCLUSIVE_INCLUSIVE);
            word4.setSpan(new StrikethroughSpan(), m.start(), m.end(),
                    Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        }
        textHighlight.setText(word4);

// Set numbers, URLs and E-mail address to be clickable with TextView#setAutoLinkMask
        final TextView textClickable = (TextView) findViewById(R.id.text_view_font_5); 
        final String contact = "Email: mvp@microsoft.com\n" +
                "Phone: +47-24885883\n" +
                "Fax: +47-24885883\n" +
                "HTTP: www.microsoft.com/mvp.asp";
        // Set the attribute first, then set the text. Otherwise, it won't work
        textClickable.setAutoLinkMask(Linkify.ALL); // or set 'android:autoLink' in layout xml
        textClickable.setText(contact);
    }
}

The results:

(0)

相关推荐

  • 深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格

    背景介绍在开发应用过程中经常会遇到显示一些不同的字体风格的信息犹如默认的LockScreen上面的时间和充电信息.对于类似的情况,可能第一反应就是用不同的多个TextView来实现,对于每个TextView设置不同的字体风格以满足需求. 这里推荐的做法是使用android.text.*;和android.text.style.*;下面的组件来实现RichText:也即在同一个TextView中设置不同的字体风格.对于某些应用,比如文本编辑,记事本,彩信,短信等地方,还必须使用这些组件才能达到想到

  • Android UI设计系列之HTML标签实现TextView设置中文字体加粗效果(6)

    搞软件开发的都知道项目中各种需求都有,而有时候各种奇葩的需求真是让人大跌眼镜,为了实现这些奇葩的需求我们往往苦逼的废寝忘食,我现在的项目中就有一个应该算得上奇葩的需求吧,需求是这样的:在一段文字中实现对部分文字加粗 这个看上去也不难并且有点小儿科,因为TextView中有个属性是Android:textStyle,它其中一个值是bold,也就是说是对将要显示的文字进行加粗,于是我就在TextView控件中添加了此属性,代码如下: <TextView android:layout_width=&quo

  • android TextView设置中文字体加粗实现方法

    英文设置加粗可以在xml里面设置: 复制代码 代码如下: <SPAN style="FONT-SIZE: 18px">android:textStyle="bold"</SPAN> 英文还可以直接在String文件里面直接这样填写: 复制代码 代码如下: <string name="styled_text">Plain, <b>bold</b>, <i>italic</

  • js 通过html()及text()方法获取并设置p标签的显示值

    html()方法 此方法类似于JavaScript中的innerHTML属性,可以用来读取或者设置某个元素中的HTML内容.要获取某个元素的内容,可以这样: 复制代码 代码如下: var p_html = $("p").html(); //获取p元素的HTML代码 如果需要设置某元素的HTML代码,那么也可以使用该方法,不过需要为它传递一个参数.例如要设置p元素的HTML代码,可以使用如下代码: 复制代码 代码如下: //设置p元素的HTML代码 $("p").ht

  • jquery设置text的值示例(设置文本框 DIV 表单值)

    jquery设置内容 - text().html() 以及 val() 我们将使用前一章中的三个相同的方法来设置内容: text() - 设置或返回所选元素的文本内容html() - 设置或返回所选元素的内容(包括 HTML标记)val() - 设置或返回表单字段的值 下面的例子演示如何通过 text().html() 以及 val() 方法来设置内容: 实例 复制代码 代码如下: $("#btn1").click(function(){$("#test1").te

  • jQuery操作Select选择的Text和Value(获取/设置/添加/删除)

    jQuery获取Select选择的Text和Value: 选择一项试试看 语法解释: 复制代码 代码如下: . $("#select_id").change(function(){//code...}); //为Select添加事件,当选择其中一项时触发 . var checkText=$("#select_id").find("option:selected").text(); //获取Select选择的Text . var checkValu

  • 19款Javascript富文本网页编辑器

    1. AIE (演示地址) AIE是一个开源的ajax图片编辑器,基于ExtJS与PHP ImageMagick开发,易于与博客/相册等其它应用相集成.提供调整图片大小,裁剪图片,旋转/翻转图片,应用滤镜,添加文本,添加水印等功能. 2. MarkitUp (演示地址) MarkitUp是一个轻量级,可定制,灵活的WYSIWYG Editor. 3. SmartMarkUP SmartMarkUP是一个轻量级,强大的JavaScript library,它能够将Textarea控件转换成富文本编

  • Android实现垂直跑马灯效果

    在我们开发过程中,跑马灯这个功能非常实用的,在实现这个功能的时候,这个时候我们通常需要找demo来实现这个方法,我从github上面找到这个demo感觉很好用,所以就要实现了这个功能喽MarqueeView,看这个工具类,因为我找这个类的时候是没有点击事件的,所以我给它加了一个点击事件,看这个工具类 public class MarqueeView extends ViewFlipper { private Context mContext; private List<String> noti

  • Android 自动化测试经验分享 UiObejct.getFromParent()的使用方法

    1. UiObejct.getFromParent()的用法:从这个名字就知道,就是从当前对象的父对象中查找想要的子对象,该子对象和当前对象应该是同一层级. 如上图所示:Max inactivity before lock是已知条件,4 minutes是我想动态获取的内容.那应该怎么做呢? 先看看结构:TableLayout(curent index is 2) - TableRow(has several indexes) - TextView(has 2 indexes). 1.先获取Tab

  • 精心收集的jQuery常用的插件1000

    1.accordion类 基于jQuery开发,非常简单的水平方向折叠控件. Horizontal accordion: jQuery 热点图书:www.hotbook.cn jQuery-Horizontal Accordion 具有XBOX360 blade界面风格的水平方向Accordion. jQuery-Horizontal Accordion jQuery plugin: Accordion 用于创建 折叠菜单的jQuery插件. jQuery plugin: Accordion 热

随机推荐