SpannableStringBuilder导致ellipsize失效(两者冲突)

这是一个自定义的控件,解决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);
    }

}

这是运行后的效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值