TextView两端对齐

来源:互联网 发布:伦敦生活 知乎 编辑:程序博客网 时间:2024/06/06 16:15

在res文件加下添加attrs.xml文件,用于设置styleable

<?xml version="1.0" encoding="utf-8"?><resources>    <!-- 末行对齐方式 -->    <declare-styleable name="AlignTextView">        <attr name="align" format="enum">            <enum name="left" value="0"/>            <enum name="center" value="1"/>            <enum name="right" value="2"/>        </attr>    </declare-styleable></resources>

自定义AlignTextView.java类,继承自TextView

import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Paint;import android.text.TextPaint;import android.util.AttributeSet;import android.util.TypedValue;import android.widget.TextView;import java.util.ArrayList;import java.util.List;/** * 两端对齐的text view,可以设置最后一行靠左,靠右,居中对齐 * * @author YD */public class AlignTextView extends TextView {    private float textHeight; // 单行文字高度    private float textLineSpaceExtra = 0; // 额外的行间距    private int width; // textView宽度    private List<String> lines = new ArrayList<String>(); // 分割后的行    private List<Integer> tailLines = new ArrayList<Integer>(); // 尾行    private Align align = Align.ALIGN_LEFT; // 默认最后一行左对齐    private boolean firstCalc = true;  // 初始化计算    private float lineSpacingMultiplier = 1.0f;    private float lineSpacingAdd = 0.0f;    private int originalHeight = 0; //原始高度    private int originalLineCount = 0; //原始行数    private int originalPaddingBottom = 0; //原始paddingBottom    private boolean setPaddingFromMe = false;    // 尾行对齐方式    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);        originalPaddingBottom = getPaddingBottom();        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.AlignTextView);        int alignStyle = ta.getInt(R.styleable.AlignTextView_align, 0);        switch (alignStyle) {            case 1:                align = Align.ALIGN_CENTER;                break;            case 2:                align = Align.ALIGN_RIGHT;                break;            default:                align = Align.ALIGN_LEFT;                break;        }        ta.recycle();    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        //首先进行高度调整        if (firstCalc) {            width = getMeasuredWidth();            String text = getText().toString();            TextPaint paint = getPaint();            lines.clear();            tailLines.clear();            // 文本含有换行符时,分割单独处理            String[] items = text.split("\\n");            for (String item : items) {                calc(paint, item);            }            //使用替代textview计算原始高度与行数            measureTextViewHeight(text, paint.getTextSize(), getMeasuredWidth() -                    getPaddingLeft() - getPaddingRight());            //获取行高            textHeight = 1.0f * originalHeight / originalLineCount;            textLineSpaceExtra = textHeight * (lineSpacingMultiplier - 1) + lineSpacingAdd;            //计算实际高度,加上多出的行的高度(一般是减少)            int heightGap = (int) ((textLineSpaceExtra + textHeight) * (lines.size() -                    originalLineCount));            setPaddingFromMe = true;            //调整textview的paddingBottom来缩小底部空白            setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),                    originalPaddingBottom + heightGap);            firstCalc = false;        }    }    @Override    protected void onDraw(Canvas canvas) {        TextPaint paint = getPaint();        paint.setColor(getCurrentTextColor());        paint.drawableState = getDrawableState();        width = getMeasuredWidth();        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 + (textHeight - firstHeight) / 2;        }        int paddingTop = getPaddingTop();        int paddingLeft = getPaddingLeft();        int paddingRight = getPaddingRight();        width = width - paddingLeft - paddingRight;        for (int i = 0; i < lines.size(); i++) {            float drawY = i * textHeight + 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 + textLineSpaceExtra * i, paint);            }        }    }    /**     * 设置尾行对齐方式     *     * @param align 对齐方式     */    public void setAlign(Align align) {        this.align = align;        invalidate();    }    /**     * 计算每行应显示的文本数     *     * @param text 要计算的文本     */    private void calc(Paint paint, String text) {        if (text.length() == 0) {            lines.add("\n");            return;        }        int startPosition = 0; // 起始位置        float oneChineseWidth = paint.measureText("中");        int ignoreCalcLength = (int) (width / oneChineseWidth); // 忽略计算的长度        StringBuilder sb = new StringBuilder(text.substring(0, Math.min(ignoreCalcLength + 1,                text.length())));        for (int i = ignoreCalcLength + 1; i < text.length(); i++) {            if (paint.measureText(text.substring(startPosition, i + 1)) > width) {                startPosition = i;                //将之前的字符串加入列表中                lines.add(sb.toString());                sb = new StringBuilder();                //添加开始忽略的字符串,长度不足的话直接结束,否则继续                if ((text.length() - startPosition) > ignoreCalcLength) {                    sb.append(text.substring(startPosition, startPosition + ignoreCalcLength));                } else {                    lines.add(text.substring(startPosition));                    break;                }                i = i + ignoreCalcLength - 1;            } else {                sb.append(text.charAt(i));            }        }        if (sb.length() > 0) {            lines.add(sb.toString());        }        tailLines.add(lines.size() - 1);    }    @Override    public void setText(CharSequence text, BufferType type) {        firstCalc = true;        super.setText(text, type);    }    @Override    public void setPadding(int left, int top, int right, int bottom) {        if (!setPaddingFromMe) {            originalPaddingBottom = bottom;        }        setPaddingFromMe = false;        super.setPadding(left, top, right, bottom);    }    /**     * 获取文本实际所占高度,辅助用于计算行高,行数     *     * @param text        文本     * @param textSize    字体大小     * @param deviceWidth 屏幕宽度     */    private void measureTextViewHeight(String text, float textSize, int deviceWidth) {        TextView textView = new TextView(getContext());        textView.setText(text);        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);        int widthMeasureSpec = MeasureSpec.makeMeasureSpec(deviceWidth, MeasureSpec.EXACTLY);        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);        textView.measure(widthMeasureSpec, heightMeasureSpec);        originalLineCount = textView.getLineCount();        originalHeight = textView.getMeasuredHeight();    }}

activity_main.xml布局中添加一个普通的TextView,添加一个AlignTextView

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:orientation="vertical"    tools:context="com.zcn.demo.aligntextview.MainActivity">    <TextView        android:id="@+id/text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"/>    <!--com.zcn.demo.aligntextview这个路径是你自定义AlignTextView类的路径-->    <com.zcn.demo.aligntextview.AlignTextView        android:id="@+id/text1"        android:layout_width="wrap_content"        android:layout_height="wrap_content" /></LinearLayout>

AlignTextViews的使用和正常的TextView使用一样

public class MainActivity extends AppCompatActivity {    private AlignTextView alignText;    private TextView text;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        text = (TextView)findViewById(R.id.text);        text.setText("这个是测试文本,用于观察现象,做对比使用,this is test text,for to see the different in different Controls");        alignText = (AlignTextView)findViewById(R.id.text1);        alignText.setText("这个是测试文本,用于观察现象,做对比使用,this is test text,for to see the different in different Controls");    }}