自定义收缩TextView

公司的UI都是些大神,设计起来毫不含糊,怎么好看怎么设计,没办法只能一点点实现着(UI以后给别的团队设计估计会说 某某团队的某某以前作出过了类似的效果 ... 嘎嘎嘎 ) 

1、根据UI设计有三个重点

a、消息内容是不固定的,视长度而现实不同的效果

b、收起/全文按钮与消息 content的  TextView对齐

c、content textView 消息需要左右都对齐

 

 

               <实现效果图>

2、自己梳理出的实现思路

a、全文/收起按钮的显示 是通过监听textView 内容(需要自定义 listener)

b、全文/收起按键会触发 textview 的显示, (Textiview 需要添加一个对应设置方法)

c、收起模式下 从UI的层面来看会占据 TextView 的右下方并且保持对齐(这里需要计算出最后一行的width 和 全文/收起 按键长度是否超过了  ATMOST 的最大值,把多出的内容用省略号替换掉)

d、全文模式下   从UI的层面来看会占据 TextView 的右下方并且保持对齐,而且保持textView的显示完全,需要计算并且准确换行

e、每一行的内容确定后需要保持左右靠边对齐

 

3、写代码(自定义的 FullTextView  关键的实现地方添加注释 ,默认先不考虑padding的值)

 

public class FullTextView extends AppCompatTextView {
    /**
     * 原始数据
     */
    private String oldText;
    /**
     * 全文显示内容
     */
    private StringBuffer fullText = new StringBuffer();
    /**
     * 缩略显示内容
     */
    private StringBuffer unFullText = new StringBuffer();
    /**
     * 通知外部UI 是否显示 全文/收起/不显示  按键
     */
    private FullTextListener listener;

    /**
     * 展示全文显示(默认是不显示全文)
     */
    boolean isShowFullText = false;
    /**
     * UI 给出缩略显示最大行数为 2
     */
    private final int UN_FULL_MAX_LINE = 2;
    /**
     * 最宽 长度
     */
    private int widthSize = 0;

    String TAG = FullTextView.class.getSimpleName();

    public FullTextView(Context context) {
        super(context);
    }

    public FullTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 设置原始数据
     * 此处不需要通过setText() 方法来实现,该自定义TextView 的测绘  跟绘制都是通过自定义实现
     */
    public void setShowText(String text) {
        oldText = text;
        if (widthSize > 0) {// 消息适用于 adapter 避免view 复用内容也复用了,所以需要重新计算出内容
            calculateText();
            updateUI();
        }
    }

    /**
     * 刷新UI
     */
    private void updateUI() {
        //触发 onMeasure  onLayout
        requestLayout();
        //触发  onDraw
        invalidate();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        Log.i(TAG, hashCode() + " width == " + width);
        //目前 默认为ATMOST 方式
        if (width != 0) {
            widthSize = width;
            calculateText();
            setMeasuredDimension(widthSize, getLineHeight() * (getLinesStr().length - 1) + getFirstBaselineToTopHeight() + getLastBaselineToBottomHeight());
        }
    }

    /**
     * 每一行的String 以数组的 形式返回
     *
     * @return
     */
    private String[] getLinesStr() {
        String[] strings;
        if (!isShowFullText) {
            // “\n”  最后时  split 会少一行内容
            if (unFullText.toString().endsWith("\n")) {
                unFullText.append(" ");
            }
            strings = unFullText.toString().split("\n");
        } else {
            if (fullText.toString().endsWith("\n")) {
                fullText.append(" ");
            }
            strings = fullText.toString().split("\n");

        }
        return strings;
    }

    /**
     * 计算出 缩略显示的 text
     */
    private void calculateUnFullText() {
        if (widthSize == 0) return;
        unFullText = new StringBuffer();
        Paint tvPaint = getPaint();
        float tempWidth = 0;
        //行数计数器
        int lineCount = 0;
        for (int cnt = 0; cnt != oldText.length(); ++cnt) {
            char ch = oldText.charAt(cnt);
            tempWidth += tvPaint.measureText(String.valueOf(ch));
            if (tempWidth <= widthSize) {
                unFullText.append(ch);
            } else {
                lineCount++;
                tempWidth = 0;
                --cnt;
                if (lineCount == UN_FULL_MAX_LINE) {
                    break;
                }
                unFullText.append("\n");

            }
        }
        // 总行数达不到 2行以上 特殊显示
        if (lineCount < UN_FULL_MAX_LINE) {
            listener.onShowFull(FullTextType.DAFAULT);
        } else {
            //如果是展示全文不需要处理
            if (isShowFullText) {
                listener.onShowFull(FullTextType.FULL);
            } else {
                float collapseW = tvPaint.measureText(getContext().getResources().getString(R.string.collapse));
                float omitW = tvPaint.measureText(getContext().getResources().getString(R.string.omit));
                String laststr = unFullText.toString().split("\n")[UN_FULL_MAX_LINE - 1];
                for (int i = 0; i < laststr.length(); i++) {
                    float textW = tvPaint.measureText(laststr.substring(0, laststr.length() - i));
                    if ((textW + collapseW + omitW) <= widthSize) {
                        unFullText = new StringBuffer(unFullText.substring(0, unFullText.length() - i));
                        break;
                    }
                }
                unFullText.append(getContext().getResources().getString(R.string.omit));
                listener.onShowFull(FullTextType.UNFULL);
            }
        }

    }

    /**
     * 最后的 收起二字 是否能在最后一行 内显示完全
     *
     * @return
     */
    private void calculateFullText() {
        oldText.replace("\n", "");
        fullText = new StringBuffer();
        Paint tvPaint = getPaint();
        float tempWidth = 0;
        //行数计数器
        for (int cnt = 0; cnt != oldText.length(); ++cnt) {
            char ch = oldText.charAt(cnt);
            tempWidth += tvPaint.measureText(String.valueOf(ch));
            if (tempWidth <= widthSize) {
                fullText.append(ch);
            } else {
                fullText.append("\n");
                tempWidth = 0;
                --cnt;
            }
        }
        float lastlinewidth = (tempWidth + tvPaint.measureText(getContext().getResources()
                .getString(R.string.collapse)));
        if (lastlinewidth > widthSize) {
            fullText.append("\n");
        }

    }

    /**
     * 计算  全文/收起 文本显示内容
     */
    private void calculateText() {
        calculateFullText();
        calculateUnFullText();
    }

    public boolean isShowFullText() {
        return isShowFullText;
    }

    /**
     * @param isShowFull 全文/收起 显示
     * @param updateUI   是否刷新UI
     */
    public void setShowFullText(boolean isShowFull, boolean updateUI) {
        this.isShowFullText = isShowFull;
        if (updateUI && fullText.length() > 0 && unFullText.length() > 0 && listener != null) {
            if (isShowFullText) {
                listener.onShowFull(FullTextType.FULL);
            } else {
                listener.onShowFull(FullTextType.UNFULL);
            }
            updateUI();
        }
    }

    public void setFullTextListener(FullTextListener listener) {
        this.listener = listener;
    }

    public interface FullTextListener {
        void onShowFull(FullTextType type);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        onDrawStrings(canvas, getLinesStr());
    }

    /**
     * 绘制每一行的 text
     *
     * @param canvas
     * @param strs
     */
    private void onDrawStrings(Canvas canvas, String[] strs) {
        //起始 X值
        float startX = 0;
        for (int i = 0; i < strs.length; i++) {
            float baseLine = getBaseline() + (getLineHeight() * i) + getPaddingTop();
            if (i == strs.length - 1) {
                startX = 0;
                canvas.drawText(strs[i], startX, baseLine, getPaint());
                break;
            }
            Log.i(TAG, "onDraw  strs" + i + "]=" + strs[i]);
            float lineWidth = getPaint().measureText(strs[i]);
            float pw = (widthSize - lineWidth) / (strs[i].length());
            startX = pw;
            for (int j = 0; j < strs[i].length(); j++) {
                canvas.drawText(String.valueOf(strs[i].charAt(j)), startX, baseLine, getPaint());
                startX += pw + getPaint().measureText(String.valueOf(strs[i].charAt(j)));
            }
        }
    }

}

定义了一个 枚举

public enum FullTextType {

    /**
     * 不处理
     */
    DAFAULT,
    /**
     * 全文
     */
    FULL,
    /**
     * 收起
     */
    UNFULL;
}

adapter 用法关键代码(xml 对齐的可以自己拓展)

 //缓存记录 iTEM 状态 避免view复用 重复
        if (fullmap.keySet().contains(position)) {
            holder.content.setShowFullText(fullmap.get(position), false);
        } else {
            holder.content.setShowFullText(false, false);
        }

        holder.content.setShowText(msgBean.getContent());
        holder.content.setFullTextListener(new FullTextView.FullTextListener() {
            @Override
            public void onShowFull(FullTextType type) {
                switch (type) {
                    case DAFAULT:
                        holder.item_msg_show_all.setVisibility(View.GONE);
                        break;
                    case FULL:
                        holder.item_msg_show_all.setVisibility(View.VISIBLE);
                        holder.item_msg_show_all.setText(mContext.getResources().getString(R.string.collapse));
                        break;
                    case UNFULL:
                        holder.item_msg_show_all.setVisibility(View.VISIBLE);
                        holder.item_msg_show_all.setText(mContext.getResources().getString(R.string.expand));
                        break;
                }

            }
        });
        holder.item_msg_show_all.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean bb = !holder.content.isShowFullText();
                holder.content.setShowFullText(bb, true);
                fullmap.put(position, bb);
            }
        });

整理流程差多就是这样了,需要demo 的可以私信.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值