Android自定义TextView排版优化

今天的文章关于android中最常用的的控件Textview,安卓中的TextView在文字布局的时候会在每行的末尾进行智能的换行分割,特别是有标点符号等出现的时候。这个规律是怎样的我至今还没有探究过。有兴趣的大家可以自己去看一下TextView的源码。项目中需要对文字进行较舒服的排布,去掉尾部的空缺,所以我对此给出来自己的解决方案,先看效果图吧。


绿色字体是用自定义的TextView加载的文字信息,没有限制最大行数。白色文字是用android系统的TextView。黄色文字是自定义的TextView,限制了最大行数,并且设置了显示自定义省略信息,就是在布局文件中调用android:ellipsize="end"。效果还是很明显的

我这里就简单粗暴的去重写Textview的onDraw方法了,而且没有super,完全是自己用Canvas去绘画每一个文字或者符号。所以一定程度上讲,这更像是一个View,丢失了很多TextView的特性,当然为了保留更多的Textview的特性,我也在里面重写了很多方法,新加入了一些方法。代码给出来,大家可以根据自己的需要自己去拓展。有很多原来TextView的函数都失去了作用,使用的时候请注意。

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * 自定义的文字整齐排版Textview控件,能让文字相对更加的整齐
 * <p>
 * 在xml布局文件中请加入命名空间"http://hdt.hdt/hdt"用于设置以下属性(单数字不填单位):
 * lineSpacingExtra,textSize,paddingLeft,paddingRight。 宽度请设置充满父布局, 目前只支持结尾省略...
 * 
 * @author illidan.huang 杭州
 */
public class MyTextView extends TextView {
    
    private final String namespace = "http://hdt.hdt/hdt";
    private String text;
    private float textSize;
    private Paint paint1 = new Paint();
    private float paddingLeft;
    private float paddingRight;
    private float textShowWidth;
    private int textColor;
    private float lineSpace;
    private int maxLines = Integer.MAX_VALUE;
    private boolean ellipsize = false;
    private float ellipsizeLength = 0;
    private String ellipsizeString = "(未完待续...)";
    // 初始化是否需要设置高度,不加判断则会无限的递归onDraw,自己看
    private boolean needHieght = true;
    private int lineCount = 0;
    
    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        text = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "text");
        textSize = attrs.getAttributeIntValue(namespace, "textSize", 10);
        textColor = attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "textColor", Color.WHITE);
        maxLines = attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "maxLines", Integer.MAX_VALUE);
        String ell = attrs.getAttributeValue("http://schemas.android.com/apk/res/android", "ellipsize");
        ellipsize = "3".equals(ell);//android:ellipsize="end"对应的值是3,请看安卓源码
        paddingLeft = attrs.getAttributeIntValue(namespace, "paddingLeft", 0);
        paddingRight = attrs.getAttributeIntValue(namespace, "paddingRight", 0);
        lineSpace = attrs.getAttributeIntValue(namespace, "lineSpacingExtra", 3);
        float d = context.getResources().getDisplayMetrics().density;
        textSize = d * textSize + 0.5f;
        lineSpace = d * lineSpace + 0.5f;
        if(maxLines <= 0){
            maxLines = Integer.MAX_VALUE;
        }
        paint1.setTextSize(textSize);
        paint1.setColor(textColor);
        paint1.setAntiAlias(true);
        textShowWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth() - paddingLeft - paddingRight;
        ellipsizeLength = paint1.measureText(ellipsizeString);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int lineCount = 0;
        char[] textCharArray = text.toCharArray();
        float drawedWidth = 0;
        float charWidth;
        for (int i = 0; i < textCharArray.length; i++) {
            charWidth = paint1.measureText(textCharArray, i, 1);
            // 这里是用于,设置了最大行数和末尾省略的情况下进行的判断,16完全是我凭感觉给出的数字,没有为什么
            if (ellipsize && textShowWidth - drawedWidth - ellipsizeLength < 16) {
                if (lineCount == maxLines - 1) {
                    canvas.drawText(ellipsizeString, paddingLeft + drawedWidth, (lineCount + 1) * textSize + lineSpace
                            * lineCount, paint1);
                    break;
                }
            }
            // 跳入下一行判断
            if (textShowWidth - drawedWidth < charWidth || textCharArray[i] == '\n') {
                lineCount++;
                if (lineCount > maxLines - 1) {
                    lineCount--;
                    break;
                }
                drawedWidth = 0;
            }

            canvas.drawText(textCharArray, i, 1, paddingLeft + drawedWidth, (lineCount + 1) * textSize + lineSpace * lineCount,
                    paint1);
            drawedWidth += charWidth;
        }
        if (needHieght) {
            setHeight((lineCount + 1) * (int) (textSize + lineSpace));
            needHieght = false;
        }
        this.lineCount = lineCount;
    }

    @Override
    public void invalidate() {
        needHieght = true;
        super.invalidate();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        // TODO Auto-generated method stub
        super.setText(text, type);
        this.text = String.valueOf(text);
        invalidate();
    }

    /**
     * 设置省略显示内容,默认"。。。"
     * 
     * @param ellString
     */
    public final void setEllipsizeString(String ellString) {
        this.ellipsizeString = ellString;
    }

    /**
     * 设置结尾是否显示省略内容
     * 
     * @param isEnd
     */
    public final void setEllipsizeEnd(boolean isEnd) {
        this.ellipsize = isEnd;
    }

    @Override
    public void setMaxLines(int maxlines) {
        this.maxLines = maxlines;
        if(this.maxLines <= 0){
            this.maxLines = Integer.MAX_VALUE;
        }
        super.setMaxLines(maxlines);
    }

    @Override
    public CharSequence getText() {
        return this.text;
    }

    /**
     * 设置行间距
     * 
     * @param spa
     */
    public void setLineSpacingExtra(float spa) {
        this.lineSpace = spa;
    }

    @Override
    public int getLineCount() {
        return lineCount;
    }

    @Override
    public int getLineHeight() {
        return (int) (textSize+lineSpace);
    }

    @Override
    public float getTextSize() {
        return textSize;
    }

    @Override
    public void setSingleLine() {
        setSingleLine(true);
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        if(singleLine){
            setMaxLines(1);
        }else{
            setMaxLines(Integer.MAX_VALUE);
        }
    }

    @Override
    public void setTextColor(int color) {
        this.textColor = color;
        paint1.setColor(color);
        super.setTextColor(color);
    }

}

英文的实现效果我这里暂时未考虑到,需要的话,我给个思路。就是在onDraw函数中,对文字进行分词的时候

char[] textCharArray = text.toCharArray(); 按单词为单位进行分割,而不是一个字符char。

以下是图示的布局文件


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:hdt="http://hdt.hdt/hdt"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black"
    android:gravity="center_horizontal"
    android:orientation="vertical" >


    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >


        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical" >


            <com.wigit.MyTextView
                android:id="@+id/txt1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                hdt:paddingLeft="5"
                android:text="【一人之下,万人之上】《意林》卷一引《六韬》:“屈一人下,伸万人上,惟圣人能行之。”《汉书·萧何传》:“夫能诎於一人之下,而信於万乘之上者,汤武是也。”一人,谓天子;万人,谓百官。多指地位崇高权势显赫的大臣。【一人之交】好友;至交。谓亲密如一人。【一夫之用】谓仅能当一人之用,而无兼人之能。【一夫之勇】犹言匹夫之勇。"
                android:textColor="#FE8A08"
                hdt:lineSpacingExtra="3"
                hdt:textSize="16" 
                ></com.wigit.MyTextView>"


            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingLeft="5dp"
                android:paddingRight="5dp"
                android:text="【一人之下,万人之上】《意林》卷一引《六韬》:“屈一人下,伸万人上,惟圣人能行之。”《汉书·萧何传》:“夫能诎於一人之下,而信於万乘之上者,汤武是也。”一人,谓天子;万人,谓百官。多指地位崇高权势显赫的大臣。【一人之交】好友;至交。谓亲密如一人。【一夫之用】谓仅能当一人之用,而无兼人之能。【一夫之勇】犹言匹夫之勇。"
                android:textColor="@android:color/white"
                android:textSize="16dp" />
            
            <com.wigit.MyTextView
                android:id="@+id/txt1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                hdt:paddingLeft="5"
                android:text="【一人之下,万人之上】《意林》卷一引《六韬》:“屈一人下,伸万人上,惟圣人能行之。”《汉书·萧何传》:“夫能诎於一人之下,而信於万乘之上者,汤武是也。”一人,谓天子;万人,谓百官。多指地位崇高权势显赫的大臣。【一人之交】好友;至交。谓亲密如一人。【一夫之用】谓仅能当一人之用,而无兼人之能。【一夫之勇】犹言匹夫之勇。"
                android:textColor="#FE8A08"
                hdt:lineSpacingExtra="3"
                hdt:textSize="16" 
                android:maxLines="5"
                android:ellipsize="end"
                ></com.wigit.MyTextView>"
        </LinearLayout>
    </ScrollView>


</LinearLayout>

若有疑问请下面留言。

若有不足请留言或者联系我,hdtpjhz@163.com


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Android自定义TextView显示全部内容,可以使用以下两种方法: 1. 使用setEllipsize()方法 通过设置setEllipsize()方法,可以在TextView的末尾添加省略号,从而指示文本被截断。你可以使用以下代码来实现: ``` yourTextView.setEllipsize(TextUtils.TruncateAt.END); yourTextView.setSingleLine(true); ``` 上述代码将设置TextView只显示一行并在末尾添加省略号。 2. 自定义TextView 你可以从TextView类继承一个新类,并覆盖onMeasure()方法以测量控件的高度和宽度。 你可以使用以下代码实现: ``` public class CustomTextView extends TextView { public CustomTextView(Context context) { super(context); } public CustomTextView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取TextView的内容 CharSequence text = getText(); if (text != null) { //测量TextView的高度 int width = getMeasuredWidth(); int height = getMeasuredHeight(); int lineCount = getLineCount(); int lineHeight = getLineHeight(); int totalHeight = lineCount * lineHeight; if (totalHeight > height) { setMeasuredDimension(width, totalHeight); } } } } ``` 上述代码将测量TextView的高度,如果文本的高度超出了TextView的高度,则调整TextView的高度以适应文本。然后你可以使用此自定义TextView来显示你的文本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值