Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
GallenShao opened this issue Aug 7, 2017 · 6 comments

Comments

@GallenShao
Copy link

GallenShao commented Aug 7, 2017

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 GallenShao changed the title 借用你的思路和框架,修复了空行、偶尔setText无效的bug 借用你的思路和框架,修复了空行、偶尔setText无效、padding设置的bug Aug 7, 2017
@MarkHan1213
Copy link

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

@GallenShao
Copy link
Author

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

@MarkHan1213
Copy link

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

@GallenShao
Copy link
Author

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

@MarkHan1213
Copy link

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

@androiddevelop
Copy link
Owner

我看下,把问题修正下。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants