EditText选择背景自定义

默认的选择背景绘制流程

TextView.java

@Override
protected void onDraw(Canvas canvas) {
    …
    Path highlight = getUpdatedHighlightPath();
    if (mEditor != null) {
        mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);
    } else {
        layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
    }
    …
}

背景其实就是是绘制Path,path由getUpdatedHightlightPath方法获取

private Path getUpdatedHighlightPath() {
    Path highlight = null;
    Paint highlightPaint = mHighlightPaint;

    final int selStart = getSelectionStart();
    final int selEnd = getSelectionEnd();
    if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) {
        if (selStart == selEnd) {
           …
        } else {
            if (mHighlightPathBogus) {
                if (mHighlightPath == null) mHighlightPath = new Path();
                mHighlightPath.reset();
                mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
                mHighlightPathBogus = false;
            }

            // XXX should pass to skin instead of drawing directly
            highlightPaint.setColor(mHighlightColor);
            highlightPaint.setStyle(Paint.Style.FILL);

            highlight = mHighlightPath;
        }
    }
    return highlight;
}

核心是利用Layout的getSelectionPath生成Path,其余就是设置Path的填充色和绘制模式

Layout.java

public void getCursorPath(int point, Path dest,
                          CharSequence editingBuffer) {
    dest.reset();

    int line = getLineForOffset(point);
    int top = getLineTop(line);
    int bottom = getLineTop(line+1);

    …

    if (Float.compare(h1, h2) == 0) {
        dest.moveTo(h1, top);
        dest.lineTo(h1, bottom);
    } else {
        …
    }

    if (caps == 2) {
        dest.moveTo(h2, bottom);
        dest.lineTo(h2 - dist, bottom + dist);
        dest.lineTo(h2, bottom);
        dest.lineTo(h2 + dist, bottom + dist);
    } else if (caps == 1) {
        …
    }

    if (fn == 2) {
        dest.moveTo(h1, top);
        dest.lineTo(h1 - dist, top - dist);
        dest.lineTo(h1, top);
        dest.lineTo(h1 + dist, top - dist);
    } else if (fn == 1) {
        …
    }
}

绘制就是一堆lineTo组成选择区域的不规则形状

自定义方法

自定义原理

源码中预留了自定义的扩展

Layout.java

public void drawBackground(Canvas canvas, Path highlight, Paint highlightPaint,
        int cursorOffsetVertical, int firstLine, int lastLine) {
    // First, draw LineBackgroundSpans.
    // LineBackgroundSpans know nothing about the alignment, margins, or
    // direction of the layout or line.  XXX: Should they?
    // They are evaluated at each line.
    if (mSpannedText) {
        if (mLineBackgroundSpans == null) {
            mLineBackgroundSpans = new SpanSet<LineBackgroundSpan>(LineBackgroundSpan.class);
        }

        Spanned buffer = (Spanned) mText;
        int textLength = buffer.length();
        mLineBackgroundSpans.init(buffer, 0, textLength);

        if (mLineBackgroundSpans.numberOfSpans > 0) {
            …
            for (int i = firstLine; i <= lastLine; i++) {
                …
                for (int n = 0; n < spansLength; n++) {
                    LineBackgroundSpan lineBackgroundSpan = (LineBackgroundSpan) spans[n];
                    lineBackgroundSpan.drawBackground(canvas, paint, 0, width,
                            ltop, lbaseline, lbottom,
                            buffer, start, end, i);
                }
            }
        }
        mLineBackgroundSpans.recycle();
    }

    // There can be a highlight even without spans if we are drawing
    // a non-spanned transformation of a spanned editing buffer.
    if (highlight != null) {
        if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
        canvas.drawPath(highlight, highlightPaint);
        if (cursorOffsetVertical != 0) canvas.translate(0, -cursorOffsetVertical);
    }
}

看出背景绘制分为两步,先绘制LineBackgroundSpan,然后绘制之前提及的Path。Path是默认的绘制,LineBackgroundSpan就是自定义的入口了

public interface LineBackgroundSpan
extends ParagraphStyle
{
    public void drawBackground(Canvas c, Paint p,
                               int left, int right,
                               int top, int baseline, int bottom,
                               CharSequence text, int start, int end,
                               int lnum);
}

实现LineBackgroundSpan,自由的绘制吧。

注意点

1.使用自定义后,防止背景重叠,要把Path的颜色改为透明

 mEditText.setHighlightColor(Color.TRANSPARENT);

2.设置span的参数问题。setSpan要一行行的设置,开始和结束必须是不同值(不然不会绘制),这里行的计算和位置的计算都是Layout中的方法。

Layout layout = mEditText.getLayout();
mEditText.getEditableText().setSpan(tempSpan, layout.getLineStart(line), layout.getLineEnd(line), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);


猜你喜欢

转载自blog.csdn.net/firedancer0089/article/details/80080493