最近在编写一个日志APP,要求对EditText里的文本进行实时的文本编辑和格式编辑(加粗、下划线、颜色、字号等),以及能够实现撤销和重做,我使用了操作队列和控制指针来完成这件事。有必要记录一下整个过程中遇到的问题。
一、Span的部分修改
首先看下面四种情况,代表了同一个字符串上加载了不同数量的加粗span,也就是StyleSpan(Typeface.BOLD)。
0123456789
0123456789
0123456789
0123456789
在文本编辑器中,假如我们此时选中了子字符串“3456”,并且按下加粗按钮B,那么理论上上述字符串应该变为:
0123456789
0123456789
0123456789
0123456789
那么在代码层面,我们要怎么实现这样的部分操作呢?
我们注意到,这个操作实际上涉及到了区间内所有span标签的修改、融合或者删除,那么从编程的角度上讲,可以用下面的删除-重新绘制思路:
- 获取区间[start, stop]内所有对应类别(例如这里是加粗)的span标签
- 记录最左标签的起点oStart和最右标签的终点oStop(也就是span标签的最小起点和最大终点)
- 删除所有span标签
- 判断左边界和右边界的格式是否与区间内新的格式相同,如果左边界相同,那么start更新为oStart,如果右边界相同,那么stop更新为oStop (中span扩展)
- 在区间[start, stop]内绘制span标签
- 如果(start!=oStart)那么在在区间[oStart, start]内绘制span标签,如果(stop!=oStop)那么在区间[stop, oStop]内绘制span标签
- 结束更新
SpannableStringBuilder spanText; //待修改的对象
int newType; //待添加的格式
int oStart = start;
int oStop = stop;
int leftType;
int rightType;
StyleSpan[] T = spanText.getSpans(start, stop, StyleSpan.class); //获取区间内所有的加粗span对象
for (int i=T.length-1; i>=0; i--) {
if (spanText.getSpanStart(T[i]) < oStart){
oStart = spanText.getSpanStart(T[i]);
leftType = T[i].getStyle();
}
if (spanText.getSpanEnd(T[i]) > oStop) {
oStop = spanText.getSpanEnd(T[i]);
rightType = T[i].getStyle();
}
spanText.removeSpan(T[i]);
//移除这些对象,注意,这里i是从大到小变化
}
if (leftType==newType) start = oStart;
else if (leftType!=0) spanText.setSpan(new StyleSpan(leftType), oStart, start, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);//绘制左span
if (rightType==newType) stop = oStop;
else if (rightType!=0) spanText.setSpan(new StyleSpan(rightType), stop, oStop, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);//绘制右span
if (newType!=0) spanText.setSpan(new StyleSpan(newType), start, stop, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);//绘制中span
结果就不展示了,带加粗的文本编辑器到处都是。