SpannableStringBuilder导致ellipsize失效(两者冲突)
程序员文章站
2022-05-15 10:22:30
...
这是一个自定义的控件,解决SpannableStringBuilder与ellipsize属性冲突的BUG,适用于ListView等有复用的列表里面,效果图在最底部。
package com.laka.jiawawa.ui.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.support.v7.widget.AppCompatTextView;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import com.laka.jiawawa.R;
import com.laka.jiawawa.util.Log;
import com.laka.jiawawa.util.ResourceHelper;
import com.laka.jiawawa.util.Utils;
import java.util.HashMap;
/**
* @Author Lyf
* @CreateTime 2017/12/12
* @Description
**/
public class SpannableTextView extends AppCompatTextView {
private String mOriginalText; // 原文本
private final String mEllipsizedString = "..."; // 省略文本
private boolean isEllipsized = false; // 是否有添加过省略字符
private String mExchangeCoins = null; // 追加的部分文本(样式与主文本不同)
// 设置部分字体的大小、颜色
private AbsoluteSizeSpan mAbsoluteSizeSpan;
private ForegroundColorSpan mForegroundColorSpan;
private SpannableStringBuilder mSpannableStringBuilder;
// 保存已求得的最佳省略文本,Key是原文本,Value是最佳省略文本
private HashMap<String, String> mEllipsizedMap = new HashMap<>();
public SpannableTextView(Context context) {
super(context);
initView();
}
public SpannableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public SpannableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView(){
mSpannableStringBuilder = new SpannableStringBuilder();
mForegroundColorSpan = new ForegroundColorSpan(ResourceHelper.getColor(R.color.colorFFC038));
mAbsoluteSizeSpan = new AbsoluteSizeSpan((int) (11 * getContext().getResources().getDisplayMetrics().density + 0.5f));
}
/**
* @param exchangeCoins 追加的部分文本(样式与主文本不同)
*/
public void setExchangeCoins(String exchangeCoins) {
// list 复用item时,需要重置状态
isEllipsized = false;
if (Utils.isNotEmpty(exchangeCoins)) {
this.mExchangeCoins = exchangeCoins;
// 求得原文本
mOriginalText = getText() + mExchangeCoins;
// 如果有最佳的省略文本,就直接设置.
if (mEllipsizedMap.get(mOriginalText) != null) {
setText(mEllipsizedMap.get(mOriginalText));
} else {
setText(mOriginalText);
}
invalidate();
}
}
@Override
protected void onDraw(Canvas canvas) {
// 有设置追加文本的,才进行计算
if (Utils.isNotEmpty(mExchangeCoins)) {
CharSequence charSequence = getText();
// 重置状态
mSpannableStringBuilder.clear();
// 追加部分文本的长度
int length = mExchangeCoins.length();
// 被省略的文本的长度
int ellipsisCount = getLayout().getEllipsisCount(getLineCount() - 1);
// 如果省略的文本长度大于0并且当前原文本没有对应的最佳省略文本,则进行计算
if (ellipsisCount > 0 && mEllipsizedMap.get(mOriginalText) == null) {
// 是否有添加过省略字符
if (isEllipsized) {
//消除被省略掉的文本,并以mEllipsizedString显示
charSequence = deleteEllipsizeText(charSequence,ellipsisCount,length);
} else {
// 已添加省略字符
charSequence = setEllipsizedString(charSequence,length);
}
} else {
// 设置文本
charSequence = (charSequence.subSequence(0, charSequence.length() - length)) + mExchangeCoins;
// 保存最佳省略文本
mEllipsizedMap.put(mOriginalText,charSequence.toString());
}
// 设置样式
setSpannable(charSequence,length);
}
super.onDraw(canvas);
}
/**
* 消除被省略掉的文本,并以mEllipsizedString显示
*/
private String deleteEllipsizeText(CharSequence charSequence , int ellipsisCount ,int length) {
// 获取主标题(不包含追加文本)
charSequence = charSequence.subSequence(0, charSequence.length() - length);
//Log.log("削减前="+charSequence);
// 循环,从省略字符左右不断削减字符,直到被省略的字符数为0.
while (ellipsisCount > 0){
int midIndex = charSequence.toString().indexOf(mEllipsizedString);
// 省略字符的左边
int startLength = charSequence.subSequence(0, midIndex).length();
// Log.log("省略符左边的文本="+charSequence.subSequence(0, midIndex));
// 省略字符的右边
int endLength = charSequence.subSequence(midIndex + 3, charSequence.length()).length();
// Log.log("省略符右边的文本="+charSequence.subSequence(midIndex + 3, charSequence.length()));
// 省略字符的左边的文本大于右边的,则削减左边的文本
if (startLength >= endLength) {
// Log.log("左边的文本大于右边");
charSequence = charSequence.subSequence(0, midIndex - 1).toString() + charSequence.subSequence(midIndex , charSequence.length()).toString();
} else {
// Log.log("右边的文本大于左边");
// 省略字符的右边的文本大于左边的,则削减右边的文本
charSequence = charSequence.subSequence(0, midIndex + 3).toString() + charSequence.subSequence(midIndex + 4, charSequence.length()).toString();
}
--ellipsisCount;
// Log.log("削减后(循环中)="+charSequence);
}
//Log.log("削减后(循环已结束,最终结果)="+charSequence);
// 削减完毕,将追加部分添加回来
return charSequence + mExchangeCoins;
}
/**
* 已添加省略字符
*/
private String setEllipsizedString(CharSequence charSequence , int length) {
isEllipsized = true;
// 求得主标题的长度(不包含追加文本)
int middle = (charSequence.length() -length) / 2;
// 去掉主标题中间四字个字符换成省略字符
String startTemp = charSequence.subSequence(0, middle - 2) + mEllipsizedString;
return startTemp + charSequence.subSequence(middle + 2, charSequence.length());
}
/**
* 设置样式
* @param mExchangeCoinsLength 追加文本的长度
*/
private void setSpannable(CharSequence charSequence ,int mExchangeCoinsLength) {
// 设置文本
mSpannableStringBuilder.append(charSequence);
int end = mSpannableStringBuilder.toString().trim().length();
int start = end - mExchangeCoinsLength;
mSpannableStringBuilder.setSpan(mForegroundColorSpan, start, end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mSpannableStringBuilder.setSpan(mAbsoluteSizeSpan, start, end, Spannable.
SPAN_EXCLUSIVE_EXCLUSIVE);
setText(mSpannableStringBuilder);
}
}