Android实际开发问题10_自定义TextBasicView

代码已经上传github(github会不定期上传各种初步研究的自定义view)

GIthub地址:https://github.com/EveSum/cView

之所以要重新设计一个Textview,是因为有的时候我们仅仅只需要一个简单的Textview,不需要多复杂的,这样节省内存资源,而且还可以从中学会如何绘制各种view


绘制单行文本使用canvas.drawText

绘制多行文本使用StaticLayout.

文字的距离Paint.FontMetricsInt



package com.caoxide.cutils.component;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

import com.caoxide.cutils.R;

/**
 * @author cxd
 * @ClassName: com.caoxide.cutils.component
 * @Description: 自定义文本控件
 * @date 2016/5/26
 */

public class TextBasicView extends View {

    //文本
    private String cText;

    //文本颜色
    private int cTextColor;

    //文本大小
    private int cTextSize;

    //文本对齐方式
    private int cTextAligin;

    //类型
    private int lineType;

    //画笔
    private Paint mPaint;

    //画笔距离
    private Paint.FontMetricsInt anInt;

    //文本画笔(仅在lineType==3时有效)
    private TextPaint textPaint;

    //多行容器(仅在lineType==3时有效)
    private StaticLayout staticLayout;

    public TextBasicView(Context context) {
        super(context);
        init(null, 0);
    }

    public TextBasicView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

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

    /**
     * 初始化
     *
     * @param attrs
     * @param defStyleAttr
     */
    private void init(AttributeSet attrs, int defStyleAttr) {
        //获得属性
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.TextBasicView, defStyleAttr, 0);
        //获取文本
        cText = typedArray.getString(R.styleable.TextBasicView_cText);
        //获取颜色值
        cTextColor = typedArray.getColor(R.styleable.TextBasicView_cTextColor, Color.BLUE);
        //获取字体大小
        cTextSize = typedArray.getDimensionPixelSize(R.styleable.TextBasicView_cTextSize,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
        //获取文本对齐方式
        cTextAligin = typedArray.getInt(R.styleable.TextBasicView_cTextAlign, 1);
        //获取行数类型
        lineType = typedArray.getInt(R.styleable.TextBasicView_lineType, 3);
        //记住属性
        typedArray.recycle();

        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        if (lineType == 3) {
            if (textPaint == null) {
                textPaint = new TextPaint();
                //设置画笔大小
                textPaint.setTextSize(cTextSize);
                //设置画笔颜色
                textPaint.setColor(cTextColor);
            }
        } else {
            if (mPaint == null) {
                mPaint = new Paint();
                //设置画笔大小
                mPaint.setTextSize(cTextSize);
                //设置画笔颜色
                mPaint.setColor(cTextColor);
                //设置文本方向
                mPaint.setTextAlign(cTextAligin == 1 ? Paint.Align.LEFT : cTextAligin == 2 ? Paint.Align.RIGHT : Paint.Align.CENTER);
            }
            if (anInt == null) {
                anInt = mPaint.getFontMetricsInt();
            }
        }
    }

    /**
     * 获取多行布局
     *
     * @return
     */
    private StaticLayout getStaticLayout() {
        if (staticLayout == null) {
            staticLayout = new StaticLayout(cText, textPaint, getWidth() - getPaddingLeft() - getPaddingRight(),
                    cTextAligin == 1 ? Layout.Alignment.ALIGN_NORMAL : cTextAligin == 2 ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_CENTER,
                    1.2f, 0, false);
        }
        return staticLayout;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        initPaint();
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int width;
        int height;
        //获取控件宽度
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            float showTextLength = mPaint.measureText(cText);
            width = Math.min((int) showTextLength, widthSize);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            if (lineType == 1) {//单行显示
                height = anInt.bottom - anInt.ascent + getPaddingTop() + getPaddingBottom();
            } else if (lineType == 2) {//两行显示
                height = 2 * anInt.bottom - anInt.ascent - anInt.top + getPaddingTop() + getPaddingBottom();
            } else {//多行显示
                height = getStaticLayout().getHeight() + getPaddingBottom() + getPaddingTop();
            }
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        initPaint();
        int width = 0;
        if (cTextAligin == 1) {
            width = getPaddingLeft();
        } else if (cTextAligin == 2) {
            width = getWidth() - getPaddingRight();
        } else {
            width = (getWidth() - getPaddingLeft() - getPaddingRight()) / 2;
        }
        if (lineType == 1) {//单行显示
            String showText = getShowText(mPaint, cText);
            //获取基准线的高度(去除文本上面的音标区域,即不使用top,使用ascent)
            int height = (getHeight() - anInt.bottom + anInt.ascent) / 2 - anInt.ascent;
            //书写文字
            canvas.drawText(showText, width, height, mPaint);
        } else if (lineType == 2) {//两行显示
            //获取第一行显示文本
            String showText1 = getShowText(mPaint, cText, false);
            //获取第一行基准线的高度
            int height1 = (getHeight() + anInt.ascent + anInt.top) / 2 - anInt.bottom - anInt.ascent;
            //书写文字
            canvas.drawText(showText1, width, height1, mPaint);
            if (showText1.length() < cText.length()) {
                String str = cText.substring(showText1.length());
                String showText2 = getShowText(mPaint, str);//获取文字区域
                //获取第二行基准线的高度
                int height2 = (getHeight() - anInt.ascent - anInt.top) / 2;
                //书写文字
                canvas.drawText(showText2, width, height2, mPaint);
            }
        } else if (lineType == 3) {//多行显示
            canvas.translate(getPaddingLeft(), getPaddingTop());
            getStaticLayout().draw(canvas);
        }

    }


    /**
     * 获取显示文本
     *
     * @param mPaint 画笔
     * @param text   要书写的文本
     * @return 显示文本
     */
    private String getShowText(Paint mPaint, String text) {
        return getShowText(mPaint, text, true);
    }

    /**
     * 获取显示文本
     *
     * @param mPaint 画笔
     * @param text   要书写的文本
     * @param isEn   是否省略
     * @return 显示文本
     */
    private String getShowText(Paint mPaint, String text, boolean isEn) {
        //获取文本显示区域
        int textRextWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        //获取要书写文字的宽度
        float textWidth = mPaint.measureText(text);
        //书写文字宽度在显示区域内,直接书写即可
        if (textWidth < textRextWidth) {
            return text;
        }
        //获取省略号的宽度
        if (isEn) {
            float enWidth = mPaint.measureText("...");
            //获取除省略号的可显示区域
            textRextWidth -= enWidth;
        }
        //获取一个汉字的宽度
        float ziWidth = mPaint.measureText("字");
        //判断书写文字是否绝大部分为汉字,误差为每十个字一个不是
        if (ziWidth * text.length() <= textWidth + text.length() / 10 * ziWidth) {
            //获取显示文字宽度
            int len = (int) (textRextWidth / ziWidth);
            int dev = ((int) (textRextWidth - mPaint.measureText(text.substring(0, len - 1))));
            while (dev > ziWidth) {
                len += dev / ziWidth;
                dev = ((int) (textRextWidth - mPaint.measureText(text.substring(0, len - 1))));
            }
            return text.substring(0, len - 1) + (isEn ? "..." : "");
        } else {
            int len = (int) (textRextWidth * text.length() / textWidth);
            if (len < 1) {
                return "...";
            }
            int len_ = len - 1;
            int dev = (int) (textRextWidth - mPaint.measureText(text.substring(0, len - 1)));
            int dev_ = (int) (textRextWidth - mPaint.measureText(text.substring(0, len_ - 1)));
            while (dev > 0 || dev_ < 0) {
                if (dev > 0) {
                    int l = (int) (dev * text.length() / textWidth);
                    if (l == 0) {
                        l = 1;
                    }
                    len += l;
                    len_ = len - 1;
                } else {
                    int l = (int) (dev_ * text.length() / textWidth);
                    if (l == 0) {
                        l = 1;
                    }
                    len_ -= l;
                    len = len_ + 1;
                }
                dev = (int) (textRextWidth - mPaint.measureText(text.substring(0, len - 1)));
                dev_ = (int) (textRextWidth - mPaint.measureText(text.substring(0, len_ - 1)));
            }
            return text.substring(0, len_ - 1) + (isEn ? "..." : "");
        }
    }


}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值