自定义View-LetterView

1、效果图
UI图.jpg

自定义View效果.gif

GitHub:https://github.com/aositeluoke/LetterView

2、总体分析
2.1、动态添加View实现

  通过在代码中动态添加View的方式,虽然能够实现,但是UI层级比较多且添加View时逻辑也比较复杂,性能略逊一筹。

2.2、自定义View

  本文采用的方案是自定义View。

3、详细分析

3.1初始状态使用下划线占位
3.2每次点击某个字母时,将该字母添加到LetterView中
3.3每次点击删除按钮时,移除最后一次加入的字母
3.4支持换行

4、代码实现
4.1、测量
@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        this.mWidthMeasureSpec = widthMeasureSpec;
        this.mHeightMeasureSpec = heightMeasureSpec;

        int wSize = MeasureSpec.getSize(mWidthMeasureSpec);
        int hSize = MeasureSpec.getSize(mHeightMeasureSpec);
        int hMode = MeasureSpec.getMode(mHeightMeasureSpec);

        singleBoxW = (int) (mUnderLineWidth + mLineLineDistance);
        singleBoxH = (int) (mFontMetrics.bottom - mFontMetrics.top + mLineTextDistance);
        columnNum = wSize / singleBoxW;//列数
        mTotalLength = mAllLetters.size();
        rowNum = mTotalLength / columnNum;
        rowNum = mTotalLength % columnNum != 0 ? rowNum + 1 : rowNum;//行数

        switch (hMode) {
            case MeasureSpec.AT_MOST:
                hSize = rowNum * singleBoxH;
                break;
            case MeasureSpec.EXACTLY:
                break;
        }

        setMeasuredDimension(wSize, hSize);

    }

  在测量方法中,计算出该单词需要几行几列才能绘制完,计算出单个字母占用区域的宽高备用。

4.2、绘制
  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mAllLetters.size(); i++) {
            int row = i / columnNum;
            int column = i % columnNum;
            float startX = (column * singleBoxW) + mLineLineDistance / 2f;
            float startY = (row + 1) * singleBoxH - mUnderLineHeight / 2f;
            float stopX = (column * singleBoxW) + mLineLineDistance / 2f + mUnderLineWidth;
            float stopY = (row + 1) * singleBoxH - mUnderLineHeight / 2f;
            canvas.drawLine(startX,
                    startY,
                    stopX,
                    stopY,
                    mLinePaint);

            if (mCurLetters.size() > i) {
                mLetterPaint.getTextBounds(mCurLetters.get(i) + "", 0, 1, mTextRect);
                canvas.drawText(mCurLetters.get(i) + "", column * singleBoxW + (singleBoxW - mTextRect.width()) / 2f, row * singleBoxH + singleBoxH / 2f + baseYOff, mLetterPaint);
            }


        }
    }

  在绘制过程中,重点在于当前字母所在的坐标(行数和列数),得到坐标后,一切都好办了,通过坐标和字母盒子的宽高即可完成绘制。在这里需要谨记的是通过列表下标和总列数获取他的二维坐标(行数和列数)的计算公式。

int row = i / columnNum;
int column = i % columnNum;
5、提供一些方法
5.1、初始化
 public void initWord(String text) {
        if (TextUtils.isEmpty(text)) {
            return;
        }
        this.mText = text;
        char[] letters = mText.toCharArray();
        mAllLetters.clear();
        for (char arra : letters) {
            mAllLetters.add(arra);
        }
        requestLayout();
        invalidate();
    }

  通过initWord方法告知LetterView当前单词的长度,间接的计算出总行数和总列数。

5.2、新增字母
 /**
     * 新增
     *
     * @param c
     */
    public void append(char c) {
        if (mText.length() <= mCurLetters.size()) {
            return;
        }
        mCurLetters.add(c);
        requestLayout();
        invalidate();
    }
5.3、删除字母
/**
     * 删除
     */
    public void del() {
        if (mCurLetters.size() > 0) {
            mCurLetters.remove(mCurLetters.size() - 1);
            requestLayout();
            invalidate();
        }

    }
6、属性说明
属性名 默认值 备注
text 单词
text_color Color.WHITE 字母颜色
underline_width 60 字体下划线宽度
underline_height 4 下划线高度
underline_color Color.WHITE 下划线颜色
line_line_distance 10 下划线之间的间距
line_text_distance 20 下划线与字母的间距
line_type LINE_TYPE_SQUARE 下划线类型 圆角或方角
7、总结

  自定义View,听起来有些可怕,但是写起来感觉没有想象中的那么艰难,可能是因为没有加入触摸事件或动画才会有这样的感觉吧(笑脸)!不过,遇到需要自定义View才能解决问题时,也不要害怕,坐下来喝杯茶(躺下来也可以,哈哈),分析测量和绘制流程,寻找动画规律等,当你分析的非常详细的时候,就再也没有你解决不了的事儿了,因为粒度越细,事情越简单。

你可能感兴趣的:(自定义View-LetterView)