Android基础:android - SpannableString或SpannableStringBuilder以及string.xml文件中的整型和string型代替(转载)

简介:

转载地址:http://blog.csdn.net/fengkuanghun/article/details/7904284

背景介绍

在开发应用过程中经常会遇到显示一些不同的字体风格的信息犹如默认的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的初级应用,如果深入研究,可以发现很多奇妙的功效。

 

实例1

 

复制代码
复制代码
<?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:


示例2

一:TextView组件改变部分文字的颜色:

 

复制代码
复制代码
TextView textView = (TextView)findViewById(R.id.textview);

//方法一:textView.setText(Html.fromHtml("<font color=\"#ff0000\">红色</font>其它颜色"));

//方法二: String text = "获得银宝箱!";
 SpannableStringBuilder style=new SpannableStringBuilder(text);   
  style.setSpan(new BackgroundColorSpan(Color.RED),2,5,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);     //设置指定位置textview的背景颜色  style.setSpan(new ForegroundColorSpan(Color.RED),0,2,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);     //设置指定位置文字的颜色  textView.setText(style); 
复制代码
复制代码

二:android string.xml文件中的整型和string型代替:

 

 String text = String.format(getResources().getString(R.string.baoxiang), 2,18,"银宝箱");

对应的string.xml文件参数:

 

<string name="baoxiang">您今天打了%1$d局,还差%2$d局可获得%3$s!</string>

%1$d表达的意思是整个name=”baoxiang”字符串中,第一个整型

 

在项目开发者,经常需要把以上两者结合起来使用。可以避免很多textview的拼接,如下所示:

复制代码
复制代码
TextView textView = (TextView)findViewById(R.id.testview);

String text = String.format(getResources().getString(R.string.baoxiang), 2,18,"银宝箱");
       int index[] = new int[3];
       index[0] = text.indexOf("2");
       index[1] = text.indexOf("18");
       index[2] = text.indexOf("银宝箱");

 SpannableStringBuilder style=new SpannableStringBuilder(text);   
           style.setSpan(new ForegroundColorSpan(Color.RED),index[0],index[0]+1,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);    
           style.setSpan(new ForegroundColorSpan(Color.RED),index[1],index[1]+2,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);    
           style.setSpan(new BackgroundColorSpan(Color.RED),index[2],index[2]+3,Spannable.SPAN_EXCLUSIVE_INCLUSIVE);    
           textView.setText(style);
复制代码
相关文章
|
22天前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
|
3月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
220 1
|
5月前
|
存储 数据库 Android开发
安卓Jetpack Compose+Kotlin,支持从本地添加音频文件到播放列表,支持删除,使用ExoPlayer播放音乐
为了在UI界面添加用于添加和删除本地音乐文件的按钮,以及相关的播放功能,你需要实现以下几个步骤: 1. **集成用户选择本地音乐**:允许用户从设备中选择音乐文件。 2. **创建UI按钮**:在界面中创建添加和删除按钮。 3. **数据库功能**:使用Room数据库来存储音频文件信息。 4. **更新ViewModel**:处理添加、删除和播放音频文件的逻辑。 5. **UI实现**:在UI层支持添加、删除音乐以及播放功能。
|
1月前
|
ARouter Android开发
Android不同module布局文件重名被覆盖
Android不同module布局文件重名被覆盖
94 0
|
3月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
119 1
|
3月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
420 1
|
3月前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
94 0
|
3月前
|
存储 NoSQL 索引
MPP架构数据仓库使用问题之在ORC文件中,String类型字段是怎么进行编码的
MPP架构数据仓库使用问题之在ORC文件中,String类型字段是怎么进行编码的
|
5月前
|
缓存 Android开发 Kotlin
【安卓app开发】kotlin Jetpack Compose框架 | 先用OKhttp下载远程音频文件再使用ExoPlayer播放
使用 Kotlin 的 Jetpack Compose 开发安卓应用时,可以结合 OkHttp 下载远程音频文件和 ExoPlayer 进行播放。在 `build.gradle` 添加相关依赖后,示例代码展示了如何下载音频并用 ExoPlayer 播放。代码包括添加依赖、下载文件、播放文件及简单的 Compose UI。注意,示例未包含完整错误处理和资源释放,实际应用需补充这些内容。
|
5月前
|
存储 Android开发 Kotlin
开发安卓app OKhttp下载后使用MediaPlayer播放
在Android Jetpack Compose应用程序中,要使用OkHttp下载远程音频文件并在本地播放,你需要完成以下几个步骤: 1. **添加依赖**:确保`build.gradle`文件包含OkHttp和Jetpack Compose的相关依赖。 2. **下载逻辑**:创建一个`suspend`函数,使用OkHttp发起网络请求下载音频文件到本地。 3. **播放逻辑**:利用`MediaPlayer`管理音频播放状态。 4. **Compose UI**:构建用户界面,包含下载和播放音频的按钮。