微信公众号:伴职创作
在应用开发过程中,图片+文字+公式显示是非常常见的一项功能。也就是尝尝提及的富文本。实现富文本显示的方式有很多种,例如采用Html显示,再例如使用SpannableString,还有就是采用第三方框架等。
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显示富文本主要难点在于显示图片问题,一下提供几种显示图片的方法:
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));
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));
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 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);
SpannableString spannableString = new SpannableString("这是文字的前景色");
ForegroundColorSpan span = new ForegroundColorSpan(Color.parseColor("#0099ee"));
spannableString.setSpan(span,5,8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);
SpannableString spannableString1 = new SpannableString("设置文字的背景色");
BackgroundColorSpan span1 = new BackgroundColorSpan(Color.parseColor("#ac00ff30"));
spannableString1.setSpan(span1,5,8,Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
textView.setText(spannableString1);
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);
SpannableString underlineString = new SpannableString("我是下划线下划线");
UnderlineSpan underlineSpan = new UnderlineSpan();
underlineString.setSpan(underlineSpan,2,8,Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(underlineString);
SpannableString strikeSpannableString = new SpannableString("我是删除线");
StrikethroughSpan StrikeSpan = new StrikethroughSpan();
strikeSpannableString.setSpan(StrikeSpan,2,5,Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(strikeSpannableString);
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);
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);
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);
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);
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