神奇的TextView-实现富文本

前言

本篇介绍一下在Android开发过程中使用最多的一个控件-TextView,实现简单的图片加载显示及链接处理。
如果你说:没有用过ConstraintLayoutFloatingActionButtonTabLayout等等,那也没多大事。但是如果说你没有用过TextView,那就得佩服你了。

先上效果:

富文本效果

TextView 家族

TextView子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Targets
TextView
Implementations of TextView
ActionMenuItemView.java
public class ActionMenuItemView extends AppCompatTextView
AppCompatAutoCompleteTextView.java
public class AppCompatAutoCompleteTextView extends AutoCompleteTextView implements
AppCompatButton.java
public class AppCompatButton extends Button implements TintableBackgroundView,
AppCompatCheckBox.java
public class AppCompatCheckBox extends CheckBox implements TintableCompoundButton {
AppCompatCheckedTextView.java
public class AppCompatCheckedTextView extends CheckedTextView {
AppCompatEditText.java
public class AppCompatEditText extends EditText implements TintableBackgroundView {
AppCompatMultiAutoCompleteTextView.java
public class AppCompatMultiAutoCompleteTextView extends MultiAutoCompleteTextView
AppCompatRadioButton.java
public class AppCompatRadioButton extends RadioButton implements TintableCompoundButton {
AppCompatTextView.java
public class AppCompatTextView extends TextView implements TintableBackgroundView,
AutoCompleteTextView.java
public class AutoCompleteTextView extends EditText implements Filter.FilterListener {
Button.java
public class Button extends TextView {
CheckBox.java
public class CheckBox extends CompoundButton {
CheckedTextView.java
public class CheckedTextView extends TextView implements Checkable {
Chronometer.java
public class Chronometer extends TextView {
CompoundButton.java
public abstract class CompoundButton extends Button implements Checkable {
DialogTitle.java
public class DialogTitle extends TextView {
DigitalClock.java
public class DigitalClock extends TextView {
EditText.java
public class EditText extends TextView {
ExtractEditText.java
public class ExtractEditText extends EditText {
MultiAutoCompleteTextView.java
public class MultiAutoCompleteTextView extends AutoCompleteTextView {
RadioButton.java
public class RadioButton extends CompoundButton {
SearchView.java
public static class SearchAutoComplete extends AppCompatAutoCompleteTextView {
Switch.java
public class Switch extends CompoundButton {
SwitchCompat.java
public class SwitchCompat extends CompoundButton {
TextClock.java
public class TextClock extends TextView {
TextView.java
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
ToggleButton.java
public class ToggleButton extends CompoundButton {

TextView有着直接或间接继承关系的有27个,如上述所示。

TextView的父类为View

TextView常用的xml属性及相关方法

TextView的属性有许多,下表展示了部分属性。

xml属性 相关方法 说明
android:autoLink setAutoLinkMask(int) 是否将符合指定格式的文本转换为可单击的超链接形式
android:autoText setKeyLinstener(KeyLinstener) 控制是否将的URL、email等地址自动转换为可单击的链接
android:linksClickable setLinksClickable(boolean) 控制该文本框的URL、email等链接是否具可以点击
android:capitalize setKeyLinstener(KeyLinstener) 控制是否将用户输入的文本转换为大写字母
android:cursorVisible setCursorVisiblek(boolean) 设置该文本框的光标是否可见
android:drawableBottom setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框底端处绘制图像
android:drawableTop setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框顶端处绘制图像
android:drawableEnd setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框结尾处绘制图像
android:drawableLeft setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框左边处绘制图像
android:drawableRight setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框右边处绘制图像
android:drawablePadding setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 设置文本框内文本与图形的间距
android:drawableStart setCompoundDrawablesWithIntrinsicBounds(Drawable,Drawable,Drawable,Drawable) 在文本框开始处绘制图像
android:editable 设置文本框是否允许被编辑
android:ellipsize setEllipsize(TextUitls,TruncateAt) 当显示文本超过了TextView的宽度时,如何处理文本
android:ems setEms(int) 设置文本框的宽度
android:height setHeight(int) 设置文本框的高度
android:fontFamily setTypeface(Typeface) 设置文本框内文本的字体
android:gravity setGravity(int) 设置文本框内文本的对齐方式
android:hint setHint(int) 设置文本框内容为空时,文本框内默认显示的提示文字
android:inputType setRawInputType(int) 设置文本框输入方式
android:lines setLines(int) 设置文本框默认占几行
android:maxEms setMaxEms(int) 设置文本框最大宽度
android:maxHeight setMaxHeight(int) 设置文本框最大高度
android:maxLenghth setMaxLenghth(int) 设置文本框最大字符长度
android:maxWidth setMaxWidth(int) 设置文本框最大宽度
android:maxLines setMaxLines(int) 设置文本框最多占几行
android:maxEms setMaxEms(int) 设置文本框最小宽度
android:minHeight setMinHeight(int) 设置文本框最小高度
android:minLenghth setMinxLenghth(int) 设置文本框最小长度
android:minWidth setMinWidth(int) 设置文本框最小宽度
android:minLines setMinLines(int) 设置文本框最少占几行
android:passwordd setTransformatinMethod(TransformatinMethod) 设置文本框是一个密码框
android:phoneNumber setKeyListener(KeyListener) 设置文本框只接受电话号码
android:scrollHorizontally setHorizontallyScrolling(boolean) 设置文本框不够显示全部内容时是否允许水平滚动
android:selectAllOnFocus setSelectAllOnFocus(boolean) 如果文本框内的内容可选择,设置是否当它获得焦点时自动选择所有文本
android:shawdowColor setshadowLayer(float,float,float,int) 设置文本框内文本的阴影的颜色
android:shawdowDx setshadowLayer(float,float,float,int) 设置文本框内文本的阴影在水平方向的偏移
android:shawdowDy setshadowLayer(float,float,float,int) 设置文本框内文本的阴影在垂直方向的偏移
android:shawdowRadius setshadowLayer(float,float,float,int) 设置文本框内文本的阴影的模糊程度,值越大,阴影越模糊
android:singleLine setTransformationMethod() 设置文本框是否为单行模式
android:text setText(CharSequence) 设置是否将文本框文本的内容
android:textAllCaps setAllCaps(boolean) 设置是否将文本框中所有字幕显示为大写字母
android:textAppearance 设置文本框的颜色、字体、大小等样式
android:textColor setTextColor(ColorStateList) 设置文本框中文本的颜色
android:textColorHighlight setHighlightColor(int) 设置文本框被选中时的颜色
android:textColorHint setHintTextColor(int) 设置文本框中提示文本的颜色
android:textColorLink setLinkTextColor(int) 设置文本框中链接的颜色
android:textIsSelectable setTextSelectedable() 设置该文本框不能编辑时,文本框内的文本是否可以被选中
android:textSize setTextSize(float) 设置文本框的字体大小
android:textStyle setTypeface(Typeface) 设置文本框内字体风格,如粗体、斜体等
android:width setWidth(int) 设置该文本框的宽度

加载图片

1
TextView.setText(CharSequence text)

由于TextView.setText传入的是CharSequence类型,那就可以配合CharSequence的子类SpannedHtml.fromHtml方法实现图片加载显示的功能。

具体的代码也很简单


1
2
3
//使用Html.fromHtml解析标签,
//asyncImageGetter用于加载img标签图片资源
Spanned spanned = Html.fromHtml(text, asyncImageGetter, null);

1
2
3
public interface OnImageClickListener {
void imageClicked(List<String> imageUrls, int position);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
    /**
* 加载图片资源
*/
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
final URLDrawable urlDrawable = new URLDrawable(getContext());

if (source.contains("storage/emulated") || source.toLowerCase().contains("/sdcard/")) {
source = "file://" + source;
} else if (source.startsWith("http://")) {
// source = source;
} else {
source = "http://www.nhtzj.com" + source;
}

mLoader.loadImage(source, options, new SimpleImageLoadingListener() {


@Override
public void onLoadingStarted(String imageUri, View view) {
urlDrawable.setBounds(placeHolder.getBounds());
urlDrawable.setDrawable(placeHolder);
}

@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
}

@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);

int width;
int height;

//Bitmap转成Drawable
Drawable drawable = new BitmapDrawable(getContext().getResources(), loadedImage);

float imgH = (float) loadedImage.getHeight();
float imgW = (float) loadedImage.getWidth();

//根据需求调整图片大小,
//我这边是将图片调整为适应屏幕宽度

// if (imgW > viewWidth) {
width = viewWidth;
height = (int) (width * (imgH / imgW));
// } else {
// width = (int) imgW;
// height = (int) imgH;
// }
drawable.setBounds(0, 0, width, height);
urlDrawable.setBounds(0, 0, screenWidth, height + 5);
urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
}

@Override
public void onLoadingCancelled(String imageUri, View view) {
super.onLoadingCancelled(imageUri, view);
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
}
});


return urlDrawable;
}
};

asyncImageGetterHtml.ImageGetter 接口的一个实例,用于加载文本中的图片。

实现其中getDrawable方法,该方法的参数String sourceimg标签上的图片地址。

我这边使用了Universal Image Loader去加载图片。

设置图片点击回调

这个有点问题,请大家帮忙解决下。

就是点击图片的位置position不正确。

代码如下:

1
2
3
4
5
6
SpannableStringBuilder spannableStringBuilder;
if (spanned instanceof SpannableStringBuilder) {
spannableStringBuilder = (SpannableStringBuilder) spanned;
} else {
spannableStringBuilder = new SpannableStringBuilder(spanned);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//获取img标签,设置图片的点击事件及回调
ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
final List<String> imageUrls = new ArrayList<>();

for (int i = 0, size = imageSpans.length; i < size; i++) {
ImageSpan imageSpan = imageSpans[i];
final String imageUrl = imageSpan.getSource();
int start = spannableStringBuilder.getSpanStart(imageSpan);
int end = spannableStringBuilder.getSpanEnd(imageSpan);
imageUrls.add(imageUrl);

final int iFinal = i;
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
if (onImageClickListener != null) {
onImageClickListener.imageClicked(imageUrls, iFinal);
}
}
};

//去除在图片间默认的点击效果
ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
if (clickableSpans != null && clickableSpans.length != 0) {
for (ClickableSpan cs : clickableSpans) {
spannableStringBuilder.removeSpan(cs);
}
}

spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

先将加载图片-fromHtml中返回的spanned转成SpannableStringBuilder,用以添加点击回调ClickableSpan
之后便是从spannableStringBuilder中获取img标签,设置图片的点击事件及回调。
代码中也写了部分注释,便于理解。

这段代码就是会产生点击图片回调的position会错位,请大神们帮忙看看。

设置链接点击事件及回调

1
2
3
public interface OnUrlClickListener {
void urlClicked(String url);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 //获取a标签,设置点击事件及回调
URLSpan[] urls = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), URLSpan.class);
for (URLSpan span : urls) {
setLinkClickable(spannableStringBuilder, span);
}

/**
* 设置链接点击回调
*
* @param clickableHtmlBuilder
* @param urlSpan
*/
private void setLinkClickable(SpannableStringBuilder clickableHtmlBuilder, URLSpan urlSpan) {
int start = clickableHtmlBuilder.getSpanStart(urlSpan);
int end = clickableHtmlBuilder.getSpanEnd(urlSpan);
int flags = clickableHtmlBuilder.getSpanFlags(urlSpan);
final String url = urlSpan.getURL();
ClickableSpan clickableSpan = new ClickableSpan() {
public void onClick(View view) {
onUrlClickListener.urlClicked(url);
}
};
//将原有链接点击效果去除,因为TextView会调用URLSpan.onClick()方法进行网页跳转
clickableHtmlBuilder.removeSpan(urlSpan);
clickableHtmlBuilder.setSpan(clickableSpan, start, end, flags);
}

URLSpan源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class URLSpan extends ClickableSpan implements ParcelableSpan {

···

@Override
public void onClick(View widget) {
Uri uri = Uri.parse(getURL());
Context context = widget.getContext();
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
}
}
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
<pre>
* author : Haitao
* blog : http://www.nhtzj.com
* time : 2018/5/21
* desc : 简单富文本
* version: 2.0
* </pre>
*/
public class RichText extends AppCompatTextView {

private ImageLoader mLoader;
private DisplayImageOptions options;

private int screenWidth;
private int viewWidth;

private int widthExp;

private Drawable placeHolder, errorImage;//占位图、出错图
private OnImageClickListener onImageClickListener;//图片点击回调
private OnUrlClickListener onUrlClickListener;//URL点击回调

public RichText(Context context) {
this(context, null);
}

public RichText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public RichText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

init(context, attrs);
}

private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichText);
placeHolder = typedArray.getDrawable(R.styleable.RichText_placeHolder);
errorImage = typedArray.getDrawable(R.styleable.RichText_errorImage);


if (placeHolder == null) {
placeHolder = new ColorDrawable(Color.GRAY);
}
if (errorImage == null) {
errorImage = new ColorDrawable(Color.GRAY);
}
typedArray.recycle();

mLoader = ImageLoader.getInstance();
options = new DisplayImageOptions.Builder()
.showImageForEmptyUri(R.drawable.icon_pic_rec)
.showImageOnFail(R.drawable.icon_pic_rec)
.resetViewBeforeLoading(true)
.cacheOnDisc(true)
.imageScaleType(ImageScaleType.IN_SAMPLE_INT)
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new FadeInBitmapDisplayer(300))
.build();

screenWidth = (int) TDevice.getScreenWidth();
viewWidth = (int) (screenWidth - TDevice.dp2px(15));
screenWidth = (int) (screenWidth - TDevice.dp2px(25));

//设置Textview内的链接可点击,且Textview可滑动
setMovementMethod(LinkMovementMethod.getInstance());
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);


if (widthMode == MeasureSpec.EXACTLY) {
// Parent has told us how big to be. So be it.
widthExp = widthSize;
} else {
widthExp = widthSize;
}
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.setText(null);
}

/**
* 设置要显示的富文本内容
*
* @param text 内容
*/
public void setRichText(String text) {
//使用Html.fromHtml解析标签,
//asyncImageGetter用于加载img标签图片资源
Spanned spanned = Html.fromHtml(text, asyncImageGetter, null);
SpannableStringBuilder spannableStringBuilder;
if (spanned instanceof SpannableStringBuilder) {
spannableStringBuilder = (SpannableStringBuilder) spanned;
} else {
spannableStringBuilder = new SpannableStringBuilder(spanned);
}

//获取a标签,设置点击事件及回调
URLSpan[] urls = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), URLSpan.class);
for (URLSpan span : urls) {
setLinkClickable(spannableStringBuilder, span);
}

//获取img标签,设置图片的点击事件及回调
ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
final List<String> imageUrls = new ArrayList<>();

for (int i = 0, size = imageSpans.length; i < size; i++) {
ImageSpan imageSpan = imageSpans[i];
final String imageUrl = imageSpan.getSource();
int start = spannableStringBuilder.getSpanStart(imageSpan);
int end = spannableStringBuilder.getSpanEnd(imageSpan);
imageUrls.add(imageUrl);

final int iFinal = i;
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
if (onImageClickListener != null) {
onImageClickListener.imageClicked(imageUrls, iFinal);
}
}
};

//去除在图片间默认的点击效果
ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
if (clickableSpans != null && clickableSpans.length != 0) {
for (ClickableSpan cs : clickableSpans) {
spannableStringBuilder.removeSpan(cs);
}
}

spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

super.setText(spanned);
}

/**
* 设置链接点击回调
*
* @param clickableHtmlBuilder
* @param urlSpan
*/
private void setLinkClickable(SpannableStringBuilder clickableHtmlBuilder, URLSpan urlSpan) {
int start = clickableHtmlBuilder.getSpanStart(urlSpan);
int end = clickableHtmlBuilder.getSpanEnd(urlSpan);
int flags = clickableHtmlBuilder.getSpanFlags(urlSpan);
final String url = urlSpan.getURL();
ClickableSpan clickableSpan = new ClickableSpan() {
public void onClick(View view) {
onUrlClickListener.urlClicked(url);
}
};
//将原有链接点击效果去除,因为TextView会调用URLSpan.onClick()方法进行网页跳转
clickableHtmlBuilder.removeSpan(urlSpan);
clickableHtmlBuilder.setSpan(clickableSpan, start, end, flags);
}

/**
* 加载图片资源
*/
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
final URLDrawable urlDrawable = new URLDrawable(getContext());

if (source.contains("storage/emulated") || source.toLowerCase().contains("/sdcard/")) {
source = "file://" + source;
} else if (source.startsWith("http://")) {
// source = source;
} else {
source = "http://www.nhtzj.com" + source;
}

mLoader.loadImage(source, options, new SimpleImageLoadingListener() {


@Override
public void onLoadingStarted(String imageUri, View view) {
urlDrawable.setBounds(placeHolder.getBounds());
urlDrawable.setDrawable(placeHolder);
}

@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
}

@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);

int width;
int height;

//Bitmap转成Drawable
Drawable drawable = new BitmapDrawable(getContext().getResources(), loadedImage);

float imgH = (float) loadedImage.getHeight();
float imgW = (float) loadedImage.getWidth();

//根据需求调整图片大小,
//我这边是将图片调整为适应屏幕宽度

// if (imgW > viewWidth) {
width = viewWidth;
height = (int) (width * (imgH / imgW));
// } else {
// width = (int) imgW;
// height = (int) imgH;
// }
drawable.setBounds(0, 0, width, height);
urlDrawable.setBounds(0, 0, screenWidth, height + 5);
urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
}

@Override
public void onLoadingCancelled(String imageUri, View view) {
super.onLoadingCancelled(imageUri, view);
urlDrawable.setBounds(errorImage.getBounds());
urlDrawable.setDrawable(errorImage);
}
});


return urlDrawable;
}
};

/**
* 缩放图片
* @param bitmap
* @param scale
* @return
*/
private static Bitmap small(Bitmap bitmap, float scale) {
Matrix matrix = new Matrix();
matrix.postScale(scale, scale); //长和宽放大缩小的比例
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}

public static class URLDrawable extends BitmapDrawable {
private Drawable drawable;

public URLDrawable(Context context) {
// drawable = context.getResources().getDrawable(R.drawable.icon_pic_rec);
}

@Override
public void draw(Canvas canvas) {
if (drawable != null)
drawable.draw(canvas);
}

public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
}

/**
* 设置加载等待图
*
* @param placeHolder
*/
public void setPlaceHolder(Drawable placeHolder) {
this.placeHolder = placeHolder;
}

/**
* 设置加载错误图
*
* @param errorImage
*/
public void setErrorImage(Drawable errorImage) {
this.errorImage = errorImage;
}

/**
* 设置图片点击回调
*
* @param onImageClickListener
*/
public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
this.onImageClickListener = onImageClickListener;
}

/**
* 设置链接点击回调
*
* @param onUrlClickListener
*/
public void setOnUrlClickListenerListener(OnUrlClickListener onUrlClickListener) {
this.onUrlClickListener = onUrlClickListener;
}

public interface OnImageClickListener {
void imageClicked(List<String> imageUrls, int position);
}

public interface OnUrlClickListener {
void urlClicked(String url);
}

代码

RichText 点击查看源码

推荐

最后推荐一个不错的富文本框架

RichText

Android平台下的富文本解析器,支持Html和Markdown

坚持原创技术分享,您的支持是对我最大的鼓励!