词组高亮的 TextView 控件。额,为什么做这个.... 好吧,之前面试时公司要求的题目
- 特定词组高亮显示(中文/英文)
- 单词自动换行
- 高亮词组保持在同一行显示
[站外图片上传中...(image-c5536e-1527260878515)]
继承自 View 实现,文本都是使用画布画上去。使用两支画笔表示默认文本和高亮文本。
文本分组实现
ExtendText 表示文本单元是否高亮
private class ExtendText {
String textUnit;
boolean isHighlight;
ExtendText(String textUnit, boolean isHighlight) {
this.textUnit = textUnit;
this.isHighlight = isHighlight;
}
}
- 将原文中匹配给定的高亮词组的前后加 # 号
- 根据 # 号将原文
split
成数组 - 遍历数组,构造
ExtendText
数组
public void setDisplayedText(final String text, final List<String> highlighted) {
if (TextUtils.isEmpty(text)) {
return;
}
String s = text;
String[] words;
extendTexts.clear();
if (highlighted != null) {
for (String str : highlighted) {
s = s.replaceAll(str, "#" + str + "#");
}
words = s.split("#");
for (String str : words) {
boolean isHighlight = highlighted.contains(str);
if (isHighlight) {
ExtendText t = new ExtendText(str, true);
extendTexts.add(t);
} else {
for (String word : Arrays.asList(str.split(" "))) {
ExtendText tt = new ExtendText(word + " ", false);
extendTexts.add(tt);
}
}
}
} else {
words = s.split(" ");
for (String str : words) {
ExtendText t = new ExtendText(str, false);
extendTexts.add(t);
}
}
requestLayout();
invalidate();
}
控件不同情况下尺寸确定
在这里也学习了自定义 View 里的尺寸测量方法
View 的测量模式有3种:
- UNSPECIFIED: 表示视图的尺寸未指明,比如
wrap_content
模式下,如果此时父容器也是wrap_content
(比如父容器的ScrollView
),则需要自己计算 View 的实际占用值 - AT_MOST: 表示视图的尺寸最多达到多少,比如
match_content
,一般取测量值和View实际占用值的最小值 - EXACTLY: 表示视图的尺寸是确定的,比如
layout_width="100dp"
,一般直接返回测量值
首先是 onMeasure
里根据测量值和测量模式获取实际需要绘制的宽高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = measureWithSize(MeasureSpec.getSize(widthMeasureSpec), widthMeasureSpec);
width -= (getPaddingRight() + getPaddingLeft());
int height = measureHeightSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
测量宽度
private int measureWithSize(int defaultSize, int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
case MeasureSpec.AT_MOST:
return Math.min(defaultSize, specSize);
case MeasureSpec.EXACTLY:
return specSize;
}
return defaultSize;
}
测量高度
高度的测量比宽度多了UNSPECIFIED
模式下,自己测量了View需要的高度
private int measureHeightSize(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
//确保layout_height为wrap_content时所占高度合适
int wrapHeight = (int) (getPaddingTop() + dfPaint.getTextSize() + getPaddingBottom());
float x_draw = getPaddingLeft();
for (ExtendText t : extendTexts) {
Paint paint = t.isHighlight ? hlPaint : dfPaint;
float textLen = paint.measureText(t.textUnit);
if (x_draw + textLen > width) {
x_draw = getPaddingLeft();
wrapHeight += paint.getTextSize();
}
x_draw += textLen;
}
result = wrapHeight;
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
//父控件为Scrollview情况,直接使用wrapHeight
break;
case MeasureSpec.AT_MOST:
result = Math.min(result, specSize);
break;
case MeasureSpec.EXACTLY:
result = specSize;
}
return result;
}
绘制时自动换行
使用 Pain.measureText
测量画笔绘制文本将要的宽度,然后与空间的宽度比较判断是否需要换行,换行就增加 y 方向的坐标值。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float x_draw = getPaddingLeft();
float y_draw = getPaddingTop() + dfPaint.getTextSize();
for (ExtendText t : extendTexts) {
Paint paint = t.isHighlight ? hlPaint : dfPaint;
float textLen = paint.measureText(t.textUnit);
if (x_draw + textLen > width) {
x_draw = getPaddingLeft();
y_draw += paint.getTextSize();
}
canvas.drawText(t.textUnit, x_draw, y_draw, paint);
x_draw += textLen;
}
}
有待完善
- 词组分组实现很糙...
- 高亮词组可点击实现
网友评论