Skip to content

借用你的思路和框架,修复了空行、偶尔setText无效、padding设置的bug #29

Open
@GallenShao

Description

@GallenShao

public class AlignTextView extends AppCompatTextView {

private List<String> lines = new ArrayList<String>(); // 分割后的行
private List<Integer> tailLines = new ArrayList<Integer>(); // 尾行
private Align align = Align.ALIGN_LEFT; // 默认最后一行左对齐

private float lineSpacingMultiplier = 1.0f;
private float lineSpacingAdd = 0.0f;

String oldText;

// 尾行对齐方式
public enum Align {
    ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT  // 居中,居左,居右,针对段落最后一行
}

public AlignTextView(Context context) {
    super(context);
    setTextIsSelectable(false);
}

public AlignTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setTextIsSelectable(false);

    lineSpacingMultiplier = attrs.getAttributeFloatValue("http://schemas.android" + "" +
            ".com/apk/res/android", "lineSpacingMultiplier", 1.0f);

    int[] attributes = new int[]{android.R.attr.lineSpacingExtra};

    TypedArray arr = context.obtainStyledAttributes(attrs, attributes);

    lineSpacingAdd = arr.getDimensionPixelSize(0, 0);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);   // 计算宽度
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    if (heightMode == MeasureSpec.EXACTLY) {
        // 确定高度,直接返回
        setMeasuredDimension(getMeasuredWidth(), heightSize);
    } else {
        recalc();

        int height = lines.size() * getLineHeight();

        if (heightMode ==  MeasureSpec.AT_MOST) {
            // 最小高度
            setMeasuredDimension(getMeasuredWidth(), Math.min(heightSize, height));
        } else {
            setMeasuredDimension(getMeasuredWidth(), height);
        }
    }
}

@Override
protected void onDraw(Canvas canvas) {
    TextPaint paint = getPaint();
    paint.setColor(getCurrentTextColor());
    paint.drawableState = getDrawableState();

    Paint.FontMetrics fm = paint.getFontMetrics();
    float firstHeight = getTextSize() - (fm.bottom - fm.descent + fm.ascent - fm.top);

    int gravity = getGravity();
    if ((gravity & 0x1000) == 0) { // 是否垂直居中
        firstHeight = firstHeight + (getTextSize() - firstHeight) / 2;
    }

    int paddingTop = getPaddingTop();
    int paddingLeft = getPaddingLeft();
    int paddingRight = getPaddingRight();
    int width = getMeasuredWidth() - paddingLeft - paddingRight;

    for (int i = 0; i < lines.size(); i++) {
        float drawY = i * getLineHeight() + firstHeight;
        String line = lines.get(i);
        // 绘画起始x坐标
        float drawSpacingX = paddingLeft;
        float gap = (width - paint.measureText(line));
        float interval = gap / (line.length() - 1);

        // 绘制最后一行
        if (tailLines.contains(i)) {
            interval = 0;
            if (align == Align.ALIGN_CENTER) {
                drawSpacingX += gap / 2;
            } else if (align == Align.ALIGN_RIGHT) {
                drawSpacingX += gap;
            }
        }

        for (int j = 0; j < line.length(); j++) {
            float drawX = paint.measureText(line.substring(0, j)) + interval * j;
            canvas.drawText(line.substring(j, j + 1), drawX + drawSpacingX, drawY + paddingTop, paint);
        }
    }
}

/**
 * 设置尾行对齐方式
 *
 * @param align 对齐方式
 */
public void setAlign(Align align) {
    this.align = align;
    invalidate();
}

private void recalc() {
    String text = getText().toString();
    if (text.equals(oldText))
        return;
    oldText = text;

    TextPaint paint = getPaint();
    lines.clear();
    tailLines.clear();

    // 文本含有换行符时,分割单独处理
    String[] items = text.split("\\n");
    for (String item : items) {
        calc(paint, item);
    }
}

/**
 * 计算每行应显示的文本数
 *
 * @param text 要计算的文本
 */
private void calc(Paint paint, String text) {
    if (text.length() == 0) {
        lines.add("\n");
        return;
    }
    int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
    int startPosition = 0; // 起始位置
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < text.length(); ++i) {
        if(paint.measureText(text.substring(startPosition, i + 1)) > (float) width) {
            startPosition = i;
            this.lines.add(sb.toString());
            sb = new StringBuilder();
            i--;
        } else {
            sb.append(text.charAt(i));
        }
    }

    if(sb.length() > 0) {
        lines.add(sb.toString());
    }

    tailLines.add(Integer.valueOf(this.lines.size() - 1));
}

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions