AlignTextView icon indicating copy to clipboard operation
AlignTextView copied to clipboard

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

Open GallenShao opened this issue 8 years ago • 6 comments

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));
}

}

GallenShao avatar Aug 07 '17 21:08 GallenShao

想问一下 如果要实现加载Html里的图片还要文字两端对齐,应该怎么办

MarkHan1213 avatar Aug 16 '17 07:08 MarkHan1213

@MarkHan1213 你可以用webview 或者如果图片只需要单独显示,而不需要和文字混合显示的话,可以自己写一个AlignTextView和ImageView的ListView/RecyclerView。

GallenShao avatar Aug 18 '17 05:08 GallenShao

我现在就是有一个页面,显示的是一段可编辑的带图片的文字,然后不清楚用WebView的话字两端会不会对齐,我使用现在这个AlignTextView的话,图片是没办法出来的

MarkHan1213 avatar Aug 18 '17 05:08 MarkHan1213

@MarkHan1213 webview你可以用css控制两端对齐,这个控件的实现里面就比较暴力,就是完全自己计算并画一个一个字,所以是不支持span的

GallenShao avatar Aug 18 '17 05:08 GallenShao

是啊,看了看TextView的源码,没找到那Html有关的地方,那我使用WebView试一下,谢谢你!

MarkHan1213 avatar Aug 18 '17 05:08 MarkHan1213

我看下,把问题修正下。

androiddevelop avatar Aug 19 '17 11:08 androiddevelop