Android自定义控件垂直TextView

正常情况下TextView的文本内容是水平显示的,那如何做到让内容垂直显示呢,于是做了一些尝试,自定义控件继承TextView,重写onDraw函数,代码如下:

    @Override  
    protected void onDraw(Canvas canvas) {  
        canvas.rotate(-90);  
        canvas.translate(-getHeight(), 0);  
        super.onDraw(canvas);  
    }

 以上实现确实做到了让内容垂直显示,但是存在宽度与高度无法适配的问题,比如在指定宽度的情况下,垂直显示的文字可能会换行,这并不是我想要的结果。后来查到网上一位朋友的文章,正好解决了此问题,在此向大神致敬。做了一些整理与修改后,最终代码如下:

package com.example.sportdemo;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;

public class VerticalTextView extends View {
    private final static int DEFAULT_TEXT_SIZE = 18;
    private final static int DEFAULT_TEXT_COLOR = 0x00000000;
    private final static int DIRECTION_TTB = 0;
    private final static int DIRECTION_BTT = 1;

    private String mText = "H";
    private TextPaint mTextPaint = new TextPaint();
    private Rect mTextRect = new Rect();
    private Path path = new Path();
    private int mDirection;

    public VerticalTextView(Context context) {
        super(context);
        init();
    }

    public VerticalTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalTextView);
        CharSequence text = a.getString(R.styleable.VerticalTextView_text);
        if (text != null) {
            mText = text.toString();
        }
        int textSize = a.getDimensionPixelOffset(R.styleable.VerticalTextView_textSize, DEFAULT_TEXT_SIZE);
        if (textSize > 0) {
            mTextPaint.setTextSize(textSize);
        }
        
        int textColor = a.getColor(R.styleable.VerticalTextView_textColor, DEFAULT_TEXT_COLOR);
        mTextPaint.setColor(textColor);
        mDirection = a.getInt(R.styleable.VerticalTextView_direction, DIRECTION_BTT);
        a.recycle();
    }

    @SuppressLint("NewApi")
    private final void init() {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(DEFAULT_TEXT_SIZE);
        mTextPaint.setColor(DEFAULT_TEXT_COLOR);
        mTextPaint.setTextAlign(Align.CENTER);
    }

    public void setText(String text) {
        mText = text;
        requestLayout();
        invalidate();
    }
    
    public void setText(int resId) {
        mText = getContext().getResources().getText(resId).toString();
        requestLayout();
        invalidate();
    }

    public void setTextSize(int size) {
        mTextPaint.setTextSize(size);
        requestLayout();
        invalidate();
    }

    public void setTextColor(int color) {
        mTextPaint.setColor(color);
        invalidate();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mTextPaint.getTextBounds(mText, 0, mText.length(), mTextRect);
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        int width = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            width = specSize;
        } else {
//            width = mTextRect.height() + getPaddingLeft() + getPaddingRight();
            width = mTextRect.height();
            if (specMode == MeasureSpec.AT_MOST) {
                width = Math.min(width, specSize);
            }
        }
        return width;
    }

    private int measureHeight(int measureSpec) {
        int height = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            height = specSize;
        } else {
//            height = mTextRect.width() + getPaddingTop() + getPaddingBottom();
            height = mTextRect.width();
            if (specMode == MeasureSpec.AT_MOST) {
                height = Math.min(height, specSize);
            }
        }
        return height;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int startX = 0;
        int startY = 0;
        int stopY = getHeight();

        if (mDirection == DIRECTION_TTB) {
            startX = (getWidth() >> 1) - (mTextRect.height() >> 1);
            path.moveTo(startX, startY);
            path.lineTo(startX, stopY);
        }
        else {
            startX = (getWidth() >> 1) + (mTextRect.height() >> 1);
            path.moveTo(startX, stopY);
            path.lineTo(startX, startY);
        }
        canvas.drawTextOnPath(mText, path, 0, 0, mTextPaint);
    }
}

 Xml布局文件:

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
                
        <com.example.sportdemo.VerticalTextView 
            xmlns:demo="http://schemas.android.com/apk/res/com.example.sportdemo" 
            android:id="@+id/vtv"
            android:layout_width="40dp"
            android:layout_height="120dp"
            android:background="@color/black"            
            demo:text="helloworld"
            demo:textColor="@color/white"
            demo:textSize="20sp"
            demo:direction="bottomtotop" />
        
    </LinearLayout>

这里需要强调的是,drawTextOnPath在4.1以前的系统在开启硬件加速的情况下达不到我们想要的效果,结果就是绘制的文字不显示,因此通过以下代码禁用硬件加速。

        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }

 

纯属学习笔记,欢迎指正。 

Android自定义控件垂直TextView,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。