App下载 微信公众号

ZRichText自定义富文本显示【Android】

技术 · 移动开发 · Android/ 作者【吾非言】/ 发布于2019-2-19/ 1.05k次浏览
2019 2/19 8:0
摘要: 富文本格式(Rich Text Format, 一般简称为RTF)是由微软公司开发的跨平台文档格式。大多数的文字处理软件都能读取和保存RTF文档。如今在大多数软件中较多应用于图片和文字的交错使用,那么作为一个Android开发工程师该如何很好地开发富文本呢?

微信公众号:伴职创作

伴职创作

在应用开发过程中,图片+文字+公式显示是非常常见的一项功能。也就是尝尝提及的富文本。实现富文本显示的方式有很多种,例如采用Html显示,再例如使用SpannableString,还有就是采用第三方框架等。

一、使用Html显示富文本

Textview可以解析以下html标签(下面只是列举部分):

<a href="...">创建超文本链接
<b>黑体字
<big>字体加大
<blockquote>从两边缩进文本
<br>换行 插入换行符
<cite>引用,通常是斜体
<dfn>述语定义
<div align="...">用来排版大块HTML段落,也用于格式化表
<em>强调文本(通常是斜体加黑体)
<font size="..." color="..." face="...">设置字体大小从1到7,颜色使用名字或RGB的十六进制值
<h1>至<h6>标题
<i>斜体字
<img src="...">图片
<p>创建一个段落
<small>字体缩小
<strike>加删除线
<strong>加重文本(通常是斜体加黑体)
<sub>下标字
<sup>上标字
<tt>打字机风格的字体
<u>下划线

对于Html显示富文本主要难点在于显示图片问题,一下提供几种显示图片的方法:

  1. 显示资源图片
Html.ImageGetter imageGetter = new Html.ImageGetter() {
    public Drawable getDrawable(String source) {
        int rId = Integer.parseInt(source);
        Drawable drawable = getResources().getDrawable(rId);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        return drawable;
    }
};
String sText = "drawable中的图片:<img src=\"" + R.drawable.ic_launcher + "\" />";
textView.setText(Html.fromHtml(sText,imageGetter,null));
  1. 显示本地图片
Html.ImageGetter imageGetter = new Html.ImageGetter() {
    public Drawable getDrawable(String source) {
        Drawable drawable = Drawable.createFromPath(source);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        return drawable;
    }
};
String sText = "本地图片:<img src=\"/mnt/sdcard/temp/1.jpg\" />";
textView.setText(Html.fromHtml(sText, imageGetter, null));
  1. 显示网路图片
Html.ImageGetter imgGetter = new Html.ImageGetter() {
    public Drawable getDrawable(String source) {
        Drawable drawable = null;
        URL url;
        try {
            url = new URL(source);
            drawable = Drawable.createFromStream(url.openStream(), "");
        } catch (Exception e) {
            return null;
        }
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        return drawable;
    }
};
String sText = "网络图片:<img src=\"http://banzhi.cc/img/1.jpg\" />";
textView.setText(Html.fromHtml(sText, imgGetter, null));

最后注意Html如果要支持链接需要添加如下代码:

textView.setMovementMethod(LinkMovementMethod.getInstance());

二、使用SpannableString显示富文本

  1. 超链接 URLSpan

超链接

SpannableString spannableString = new SpannableString("为文字设置超链接");
URLSpan urlSpan = new URLSpan("http://www.jianshu.com/u/b216fb12a05d");
spannableString.setSpan(urlSpan, 5, spannableString.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setHighlightColor(Color.parseColor("#36969696"));
textView.setText(spannableString);
  1. 前景色 ForegroundColorSpan

前景色

SpannableString spannableString = new SpannableString("这是文字的前景色");
ForegroundColorSpan span = new ForegroundColorSpan(Color.parseColor("#0099ee"));
spannableString.setSpan(span,5,8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);
  1. 背景色 BackgroundColorSpan

背景色

SpannableString spannableString1 = new SpannableString("设置文字的背景色");
BackgroundColorSpan span1 = new BackgroundColorSpan(Color.parseColor("#ac00ff30"));
spannableString1.setSpan(span1,5,8,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(spannableString1);
  1. 不同字体大小 RelativeSizeSpan

不同字体大小

SpannableString spannableString2 = new SpannableString("我们是葫芦娃葫芦娃");
RelativeSizeSpan relativeSizeSpan1 = new RelativeSizeSpan(1.2f);
RelativeSizeSpan relativeSizeSpan2 = new RelativeSizeSpan(1.4f);
RelativeSizeSpan relativeSizeSpan3 = new RelativeSizeSpan(1.6f);
RelativeSizeSpan relativeSizeSpan4 = new RelativeSizeSpan(1.8f);
RelativeSizeSpan relativeSizeSpan5 = new RelativeSizeSpan(1.6f);
RelativeSizeSpan relativeSizeSpan6 = new RelativeSizeSpan(1.4f);
RelativeSizeSpan relativeSizeSpan7 = new RelativeSizeSpan(1.2f);
RelativeSizeSpan relativeSizeSpan8 = new RelativeSizeSpan(1.2f);

spannableString2.setSpan(relativeSizeSpan1,1,2,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan2,2,3,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan3,3,4,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan4,4,5,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan5,5,6,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan6,6,7,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan7,7,8,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
spannableString2.setSpan(relativeSizeSpan8,8,9,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

textView.setText(spannableString2);
  1. 下划线 UnderlineSpan

下划线

SpannableString underlineString = new SpannableString("我是下划线下划线");
UnderlineSpan underlineSpan = new UnderlineSpan();
underlineString.setSpan(underlineSpan,2,8,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(underlineString);
  1. 删除线 StrikethroughSpan

删除线

SpannableString strikeSpannableString = new SpannableString("我是删除线");
StrikethroughSpan StrikeSpan = new StrikethroughSpan();
strikeSpannableString.setSpan(StrikeSpan,2,5,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(strikeSpannableString);
  1. 上标以及字体缩小 SuperscriptSpan

上标以及字体缩小

SpannableString superString = new SpannableString("这个是上标");
SuperscriptSpan span = new SuperscriptSpan();
RelativeSizeSpan sizeSpan = new RelativeSizeSpan(0.6f);
superString.setSpan(span,3,5, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
superString.setSpan(sizeSpan,3,5,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(superString);
  1. 下标以及字体缩小 SubscriptSpan

下标以及字体缩小

SpannableString spannablestring = new SpannableString("这个是下标");
SubscriptSpan subSpan = new SubscriptSpan();
spannablestring.setSpan(subSpan,3,5,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
RelativeSizeSpan sizeSpan = new RelativeSizeSpan(0.6f);
spannablestring.setSpan(sizeSpan,3,5,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(spannablestring);
  1. 文字包含表情 ImageSpan

文字包含表情

SpannableString imageString = new SpannableString("这个文字里面包含表情(表情)");
Drawable image = getResources().getDrawable(R.mipmap.smiley_0);
image.setBounds(0,0,50,50);
ImageSpan imageSpan = new ImageSpan(image);
imageString.setSpan(imageSpan,8,10,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(imageString);
  1. 其他设置

MaskFilterSpan 修饰效果,如模糊(BlurMaskFilter)浮雕(EmbossMaskFilter)

MaskFilterSpan span = new MaskFilterSpan(new BlurMaskFilter(density*2, BlurMaskFilter.Blur.NORMAL));
MaskFilterSpan span = new MaskFilterSpan(new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f));

MetricAffectingSpan 父类,一般不用。

RasterizerSpan 光栅效果。

SuggestionSpan 相当于占位符。

AbsoluteSizeSpan 绝对大小(文本字体)。

DynamicDrawableSpan 设置图片,基于文本基线或底部对齐。

ReplacementSpan 父类,一般不用。

ScaleXSpan 基于x轴缩放。

//设置水平方向上放大3倍
ScaleXSpan span = new ScaleXSpan(3.0f);

StyleSpan 字体样式:粗体、斜体等。

//设置bold+italic的字符样式
StyleSpan span = new StyleSpan(Typeface.BOLD | Typeface.ITALIC);

//设置serif family
TypefaceSpan span = new TypefaceSpan("serif");

TextAppearanceSpan 文本外貌(包括字体、大小、样式和颜色)。

ColorStateList colors = ColorStateList.valueOf(0xff40aff2);
// style 为0 即是正常的,还有Typeface.BOLD(粗体) Typeface.ITALIC(斜体)等
// size  为0 即采用原始的正常的 size大小
TextAppearanceSpan = new TextAppearanceSpan(null, Typeface.ITALIC, 20, colors, null ), 0, 10, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);

三、使用ZRichText框架显示富文本

1、导入资源
A. gradle方式:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
    implementation 'com.github.zrunker:ZRichText:v1.0.3'
}

B. maven方式:

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependency>
    <groupId>com.github.zrunker</groupId>
    <artifactId>ZRichText</artifactId>
    <version>v1.0.3</version>
</dependency>

2、引用
A. 布局文件中引入RichTextView:

<cc.ibooker.richtext.RichTextView
        android:id="@+id/richTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

B. Activity中设置内容和操作:

public class MainActivity extends AppCompatActivity {
    private RichTextView richTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ArrayList<RichBean> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            RichBean richBean = new RichBean();
            if (i % 2 == 0) {
                richBean.setType(1);
                if (i == 0) {
                    richBean.setText("https://graph.baidu.com/resource/101de050033f9aea1f04601554958922.jpg");
                    richBean.setBackgroundColor("#40aff2");
                } else if (i == 2) {
                    richBean.setText("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=991546949,3543761346&fm=26&gp=0.jpg");
                    richBean.setColor("#55F033");
                } else if (i == 4) {
                    richBean.setText("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3421137803,3191126749&fm=26&gp=0.jpg");
                } else if (i == 6)
                    richBean.setText("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=322845814,2288534704&fm=26&gp=0.jpg");
                else if (i == 8) {
                    richBean.setText("http://img4.imgtn.bdimg.com/it/u=3735493004,329574884&fm=26&gp=0.jpg");
                } else
                    richBean.setText("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=659351332,2214018330&fm=26&gp=0.jpg");
                richBean.setWidth(0);
                richBean.setHeight(0);
            } else {
                richBean.setType(0);
                richBean.setText("伴职创作" + i + "测试富文本");
                if (i == 1) {
                    richBean.setTextSizeMultiple(1.5f);
                    richBean.setStrikethrough(true);
                    richBean.setBold(true);
                }
                if (i == 3) {
                    richBean.setAddUrl("http://banzhi.cc/article/search/list/1");
                }
                if (i == 5) {
                    richBean.setBackgroundColor("#FF4040");
                    richBean.setSuperscript(true);
                    richBean.setBoldItalic(true);
                }
                if (i == 7) {
                    richBean.setUnderline(true);
                    richBean.setItalic(true);
                }
                if (i == 9) {
                    richBean.setColor("#4aFF00");
                    richBean.setSubscript(true);
                    richBean.setScaleXMultiple(1.7f);
                }
                richBean.setOnClickSpan(new ClickSpan.OnClickSpan() {
                    @Override
                    public void onClickSpan(String txt) {
                        Toast.makeText(MainActivity.this, "文本点击事件:" + txt, Toast.LENGTH_SHORT).show();
                    }
                });
            }
            list.add(richBean);
        }

        richTextView = findViewById(R.id.richTextView);
//        richTextView.setOnLongImageSpanClickListener(new ClickSpan.OnClickSpan() {
//            @Override
//            public void onClickSpan(String txt) {
//                Toast.makeText(MainActivity.this, "长图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
//            }
//        }).setOnImageSpanClickListener(new ClickSpan.OnClickSpan() {
//            @Override
//            public void onClickSpan(String txt) {
//                Toast.makeText(MainActivity.this, "图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
//            }
//        }).setRichText(list);

//        richTextView.setOnLongImageSpanClickListener(new ClickSpan.OnClickSpan() {
//            @Override
//            public void onClickSpan(String txt) {
//                Toast.makeText(MainActivity.this, "长图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
//            }
//        }).setOnImageSpanClickListener(new ClickSpan.OnClickSpan() {
//            @Override
//            public void onClickSpan(String txt) {
//                Toast.makeText(MainActivity.this, "图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
//            }
//        }).setRichText(list, R.mipmap.ic_launcher);

        richTextView.setOnLongImageSpanClickListener(new ClickSpan.OnClickSpan() {
            @Override
            public void onClickSpan(String txt) {
                Toast.makeText(MainActivity.this, "长图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
            }
        }).setOnImageSpanClickListener(new ClickSpan.OnClickSpan() {
            @Override
            public void onClickSpan(String txt) {
                Toast.makeText(MainActivity.this, "图片点击事件,图片地址:" + txt, Toast.LENGTH_SHORT).show();
            }
        }).setRichText(list, ContextCompat.getDrawable(this, R.mipmap.ic_launcher));


        // 5s之后修改单项数据
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                RichBean richBean = list.get(0);
                richBean.setBackgroundColor("#55FF00");
                richTextView.updateItem(richBean, 0);
            }
        }, 5000);

        // 5s之后修改单项数据
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                RichBean richBean = list.get(9);
                richBean.setText("下面是加载图片对比图:");
                richTextView.updateItem(richBean, 9);
            }
        }, 10000);

        ImageView image = findViewById(R.id.image);
        Glide.with(this)
                .load("https://graph.baidu.com/resource/101de050033f9aea1f04601554958922.jpg")
                .into(image);
    }

//    @Override
//    protected void onDestroy() {
//        super.onDestroy();
//        richTextView.onDestory();
//    }
}

富文本相关数据

public class RichBean {
    private String text;// 文本或图片URL/图片文件地址
    private int res;// 图片Res
    private int type;// 0-文本,1-图片
    private int height; // 图片高
    private int width; // 图片宽
    private String backgroundColor;// 背景颜色16进制
    private String color;// 文本颜色16进制
    private String addUrl;// 添加超链接 - 针对于文本
    private float textSizeMultiple;// 字体倍数 - 针对于文本
    private boolean isUnderline;// 是否添加下划线 - 针对于文本
    private boolean isStrikethrough;// 是否添加删除线 - 针对于文本
    private boolean isSuperscript;// 是否为上标 - 针对于文本
    private boolean isSubscript;// 是否为下标 - 针对于文本
    private boolean isBold;// 是否加粗 - 针对于文本
    private boolean isItalic;// 是否斜体 - 针对于文本
    private boolean isBoldItalic;// 是否加粗并斜体 - 针对于文本
    private float scaleXMultiple;// X轴缩放倍数 - 针对于文本
    private ClickSpan.OnClickSpan onClickSpan;// 点击事件
}

最终显示效果:
图片描述
图片描述

GitHub地址:https://github.com/zrunker/ZRichText

感谢您使用伴职平台,如有侵权,请投诉删除!

全部评价

最新
查看更多评论 加载

猜你喜欢

换一批