最近公司做一个社区类的应用,发现android项目中的文字排版参差不齐的情况非常严重,不得不想办法解决一下。由于之前只做过处理简单文本的这种问题,没加上含有表情、内链等处理,由于网上的各种处理办法达到的效果并不是很满易,所以只好重写来解决这个问题。
看过源码的都知道TextView里面处理换行都是在Layout里面,一般来说换行是由于英文字符及数字导致的,ellipsize则是由于计算的长度而导致...后面会显示半个字,这是android本身的一个BUG,所以经过研究找到了里面计算字符长度的方法<font color=#0099ff>Layout.getDesiredWidth</font>,方法的英文大略意思是:返回一个指定文本的宽度<font color=#ff0000>(英文略渣)</font>
由于里面的计算是用for循环计算每个CharSequence的长度,所以直接用反射通过measurePara方法来获取宽度并判断是否达到TextView的Width
onMeasure
if(mPostContentBean != null && !mPostContentBean.mParsed){
mBuilder.clear();
if(getWidth() != 0){
try {
int contentLength = getWidth() - getPaddingLeft() - getPaddingRight();
int length = getText().length();
int lines = 0;
int start = 0;
for (int i = 0; i < length; i++) {
int end = TextUtils.indexOf(getText(), '\n', i, length);
if (end < 0)
i = length;
else
i = end + 1;
for (int j = start; j <= i; j++) {
CharSequence sequence = getText().subSequence(start, j);
mTmepBuilder.clear();
mTmepBuilder.append(sequence);
if(mMaxLines == lines + 1){
mTmepBuilder.append(mEllipsize);
}
float w = measurePara(mTmepBuilder);
if (w > contentLength || j == i){
if(w > contentLength){
j--;
}
end = sequence.length() - 1;
if(j == i){
end = sequence.length();
}
lines++;
start = j;
mBuilder.append(sequence.subSequence(0, end));
if(mMaxLines == lines){
i = length;
j = i;
mBuilder.append(mEllipsize);
}
if(j != length && mBuilder.charAt(mBuilder.length() - 1) != '\n'){
mBuilder.append("\n");
}
}
}
}
mPostContentBean.mParsed = true;
mPostContentBean.mBuilder.clear();
mPostContentBean.mBuilder.append(mBuilder);
setText(mBuilder);
} catch (Exception e) {
e.printStackTrace();
}
}
}
measurePara(SDK16以上的API是不同的)
private float measurePara(CharSequence sequence) throws Exception{
if(android.os.Build.VERSION.SDK_INT < 16){
Method measurePara = Layout.class.getDeclaredMethod("measurePara", TextPaint.class, CharSequence.class, int.class, int.class);
measurePara.setAccessible(true);
return (Float) measurePara.invoke(null, getPaint(), mPaint, sequence, 0, sequence.length());
}else{
Method measurePara = Layout.class.getDeclaredMethod("measurePara", TextPaint.class, CharSequence.class, int.class, int.class);
measurePara.setAccessible(true);
return (Float) measurePara.invoke(null, getPaint(), sequence, 0, sequence.length());
}
}