美文网首页Android开发经验谈Android开发Android技术知识
自定义view之——文本字体均匀分布控件实现

自定义view之——文本字体均匀分布控件实现

作者: JohnsonZZZ | 来源:发表于2018-01-19 14:37 被阅读119次

    效果展示

    话不多说,直接先看一波效果图。注:为了能直观显示出控件效果,我给view加了一层背景色


    效果图.png

    需求调研

    在实际开发当中,经常可以遇到一些类似于表格填写、显示的界面。一般前面是表格信息提示,后面是表格要填写的内容。这个时候问题就来了,比如说表格信息提示是“手机号”和“手机号码”的情况下,如何保持相同长度的前提下,文本字体的均匀分布呢?分析之后,需求如下。

    • 文本字体均匀分布
    • 可以自定义配置均匀文本字体后面的附属信息,比如上面截图中的“:”符号,这里要求支持一切字符串的,(比如说完整的一行显示应该是 手机号码:199****9999),当然了附属信息也可以什么都不写。
    • 支持“显瘦”的字体均匀分布,比如说数字、字母这样的组合。

    解决方案

    方案一:
    空格占位法。针对上面提出的第一个需求,如果表格界面信息提示 都是纯文字或者纯英文之类的倒也还好,直接可以参考这篇文章。通过直接使用对应字体大小的空格占位符就可以直接实现均匀分布,简单粗暴。但是如果提示信息一会是MSN、QQ这样的字母,一会是手机号码之类的中文。通过上面的占位符方法,就行不通了。

    方案二:
    针对上面方案暴露的问题,这个时候就需要自定义View进行文字测量了。那么如何实现文字均匀分布呢?思路如下:
    1).文本宽度 = 附属信息宽度 + 每个字体占有的宽度 * 文本长度 + 文本字体间距 * (文本长度 - 1)
    2).在“肥瘦不一”的字体情况下,计算出字体宽度的偏移量,这一步为了更均匀的分布字体位置。

    代码展示

    /**
     * Created by JohnsonFan on 2017/12/5.
     */
    
    public class WrapTextView extends View {
        public final String DEFALUT_LANGUAGE = "中";
        private int mTextColor;//文本颜色
        private int mTextSize;//文本大小
        private String mText;//均匀分布文本内容
        private String mTextExtra;//文本末尾附属内容
        private String[] mTextContent;//均匀分布文本内容
        private Paint mPaint;//画笔
        private float mCellPadding;//均匀字体分布间距
    
        public WrapTextView(Context context) {
            this(context, null);
        }
    
        public WrapTextView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WrapTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initAttributeSet(attrs);
        }
    
        private void initAttributeSet(AttributeSet attrs) {
            if (attrs != null) {
                TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.WrapTextView);
                mTextColor = array.getColor(R.styleable.WrapTextView_textColor, Color.BLACK);
                mTextSize = array.getDimensionPixelSize(R.styleable.WrapTextView_textSize, sp2px(14));
                mText = array.getString(R.styleable.WrapTextView_text);
                mTextExtra = array.getString(R.styleable.WrapTextView_textExtra);
                array.recycle();
    
                mPaint = new Paint();
                mPaint.setAntiAlias(true);
                mPaint.setColor(mTextColor);
                mPaint.setTextSize(mTextSize);
                initTextContent();
                mTextExtra = mTextExtra == null ? "" : mTextExtra;
            }
        }
    
        private void initTextContent() {
            mText = mText == null ? "" : mText;
            mTextContent = new String[mText.length()];
            for (int i = 0; i < mText.length(); i ++) {
                mTextContent[i] = mText.substring(i, (i + 1));
            }
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int drawY = (int) ((canvas.getHeight() / 2) - ((mPaint.descent() + mPaint.ascent()) / 2)) ;//字体垂直居中
            for (int i = 0; i < mTextContent.length; i ++) {
                float fontChangeSize = Math.abs(measureText(mTextContent[i]) - measureText(1));
                log("onDraw fontChangeSize:" + fontChangeSize);
                log(String.format("onDraw 第%d个字位置:%s", i+1, getPaddingLeft() + i * mCellPadding + measureText(i) + ""));
                canvas.drawText(mTextContent[i], getPaddingLeft() + i * mCellPadding + measureText(i) +
                        fontChangeSize / 2, drawY, mPaint);
                if (i == mTextContent.length - 1) {
                    log("onDraw extra位置:" + (getPaddingLeft() + i * mCellPadding + measureText(i + 1)));
                    if (i == 0) {
                        canvas.drawText(mTextExtra, getPaddingLeft() + mCellPadding + measureText(mTextContent.length),
                                drawY, mPaint);
                    } else {
                        canvas.drawText(mTextExtra, getPaddingLeft() + i * mCellPadding + measureText(mTextContent.length),
                                drawY, mPaint);
                    }
                }
            }
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            calculateCellPadding(w);
            log("onSizeChanged, mCellPadding:" + mCellPadding);
        }
    
        private void calculateCellPadding(int viewWidth) {
            if (mTextContent.length == 2) {
                mCellPadding = viewWidth - getPaddingLeft() - getPaddingRight() - measureText(2) - measureText
                        (mTextExtra);
            } else if (mTextContent.length > 2) {
                mCellPadding = (viewWidth - getPaddingLeft() - getPaddingRight() - measureText(mTextContent.length)
                        - mPaint.measureText(mTextExtra))/(mTextContent.length - 1);
            } else {
                mCellPadding = viewWidth - getPaddingLeft() - getPaddingRight() - measureText(1) - measureText
                        (mTextExtra) ;
            }
        }
    
        /**
         * 适配Wrap_Content
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //适配wrap_content
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
            int wrapWidth = (int) (measureText(mText.length()) + measureText(mTextExtra) +
                    getPaddingLeft() + getPaddingRight());
            int wrapHeight = (int) (measureText(1) + getPaddingTop() + getPaddingBottom());
    
            if (MeasureSpec.AT_MOST == widthMode && MeasureSpec.AT_MOST == heightMode) {
                setMeasuredDimension(wrapWidth, wrapHeight);
            } else if (MeasureSpec.AT_MOST == widthMode) {
                setMeasuredDimension(wrapWidth, heightSize);
            } else if (MeasureSpec.AT_MOST == heightMode) {
                setMeasuredDimension(widthSize, wrapHeight);
            } else {
                setMeasuredDimension(widthSize, heightSize);
            }
    
        }
    
        /**
         * 计算字符长度
         * @param text
         * @return
         */
        private float measureText(String text) {
            return mPaint.measureText(text);
        }
    
        /**
         * 获取maxLength个中文字符的长度
         * @param maxLength
         * @return
         */
        private float measureText(int maxLength) {
            return measureText(DEFALUT_LANGUAGE) * maxLength;
        }
    
        public void setText(String text) {
            mText = text;
            initTextContent();
            calculateCellPadding(getMeasuredWidth());
            invalidate();
        }
    
        public void setTextExtra(String text) {
            mTextExtra = text == null ? "" : text;
            calculateCellPadding(getMeasuredWidth());
            invalidate();
        }
    
        private int sp2px(float spValue){
            float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
            return (int) (spValue * fontScale + 0.5f);
        }
    
        private void log(String logText) {
            Log.e("WrapTextView", logText);
        }
    
    }
    

    attrs文件里增加自定义属性

    <declare-styleable name="WrapTextView">
            <attr name="text" format="string|reference" />
            <attr name="textExtra" format="string|reference" />
            <attr name="textSize" format="dimension|reference" />
            <attr name="textColor" format="color|reference" />
        </declare-styleable>
    

    xml文件调用自定义控件

    <com.example.johnsonfan.scrollapplication.WrapTextView
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            app:textExtra=":"
            app:textSize="17sp"
            app:text="手机号"/>
    

    以上便是字体均匀分布控件的全部内容,代码已上传github,有兴趣可以点击这里查看。

    相关文章

      网友评论

        本文标题:自定义view之——文本字体均匀分布控件实现

        本文链接:https://www.haomeiwen.com/subject/bxmooxtx.html