Android中的垂直(旋转)标签

我需要两种在Android中显示垂直标签的方法:

  1. 水平标签逆时针旋转90度(侧面的字母)
  2. 一个字母下的水平标签(如商店标志)

我是否需要为这两种情况(一种情况)开发自定义小部件,是否可以让TextView呈现这种方式?如果需要完全自定义,那么采用这种方法做什么是一种好方法?

这是我优雅和简单的垂直文本实现,扩展TextView。 这意味着可以使用TextView的所有标准样式,因为它是扩展的TextView。

public class VerticalTextView extends TextView{ final boolean topDown; public VerticalTextView(Context context, AttributeSet attrs){ super(context, attrs); final int gravity = getGravity(); if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP); topDown = false; }else topDown = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(heightMeasureSpec, widthMeasureSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); } @Override protected boolean setFrame(int l, int t, int r, int b){ return super.setFrame(l, t, l+(bt), t+(rl)); } @Override public void draw(Canvas canvas){ if(topDown){ canvas.translate(getHeight(), 0); canvas.rotate(90); }else { canvas.translate(0, getWidth()); canvas.rotate(-90); } canvas.clipRect(0, 0, getWidth(), getHeight(), android.graphics.Region.Op.REPLACE); super.draw(canvas); } } 

默认情况下,旋转的文本是从上到下。 如果你设置了android:gravity =“bottom”,那么它是从下到上绘制的。

从技术上讲,它欺骗底层的TextView,认为它是正常的旋转(交换宽度/高度在少数地方),而绘制旋转。 在xml布局中使用它也能正常工作。

编辑:张贴另一个版本,上面有动画的问题。 这个新版本效果更好,但丢失了一些TextView功能,比如跑马灯和类似的特色。

 public class VerticalTextView extends TextView{ final boolean topDown; public VerticalTextView(Context context, AttributeSet attrs){ super(context, attrs); final int gravity = getGravity(); if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP); topDown = false; }else topDown = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ super.onMeasure(heightMeasureSpec, widthMeasureSpec); setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); } @Override protected void onDraw(Canvas canvas){ TextPaint textPaint = getPaint(); textPaint.setColor(getCurrentTextColor()); textPaint.drawableState = getDrawableState(); canvas.save(); if(topDown){ canvas.translate(getWidth(), 0); canvas.rotate(90); }else { canvas.translate(0, getHeight()); canvas.rotate(-90); } canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop()); getLayout().draw(canvas); canvas.restore(); } } 

我为ChartDroid项目实现了这个功能。 创建VerticalLabelView.java

 public class VerticalLabelView extends View { private TextPaint mTextPaint; private String mText; private int mAscent; private Rect text_bounds = new Rect(); final static int DEFAULT_TEXT_SIZE = 15; public VerticalLabelView(Context context) { super(context); initLabelView(); } public VerticalLabelView(Context context, AttributeSet attrs) { super(context, attrs); initLabelView(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalLabelView); CharSequence s = a.getString(R.styleable.VerticalLabelView_text); if (s != null) setText(s.toString()); setTextColor(a.getColor(R.styleable.VerticalLabelView_textColor, 0xFF000000)); int textSize = a.getDimensionPixelOffset(R.styleable.VerticalLabelView_textSize, 0); if (textSize > 0) setTextSize(textSize); a.recycle(); } private final void initLabelView() { mTextPaint = new TextPaint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(DEFAULT_TEXT_SIZE); mTextPaint.setColor(0xFF000000); mTextPaint.setTextAlign(Align.CENTER); setPadding(3, 3, 3, 3); } public void setText(String text) { mText = text; 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(), text_bounds); setMeasuredDimension( measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text result = text_bounds.height() + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } return result; } private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaint.ascent(); if (specMode == MeasureSpec.EXACTLY) { // We were told how big to be result = specSize; } else { // Measure the text result = text_bounds.width() + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { // Respect AT_MOST value if that was what is called for by measureSpec result = Math.min(result, specSize); } } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float text_horizontally_centered_origin_x = getPaddingLeft() + text_bounds.width()/2f; float text_horizontally_centered_origin_y = getPaddingTop() - mAscent; canvas.translate(text_horizontally_centered_origin_y, text_horizontally_centered_origin_x); canvas.rotate(-90); canvas.drawText(mText, 0, 0, mTextPaint); } } 

attrs.xml

 <resources> <declare-styleable name="VerticalLabelView"> <attr name="text" format="string" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> </declare-styleable> </resources> 

达到这些的一个方法是:

  1. 编写自己的自定义视图并覆盖onDraw(Canvas)。 您可以在画布上绘制文字,然后旋转画布。
  2. 和1.一样,除了这次使用一个Path并使用drawTextOnPath(…)绘制文本
 check = (TextView)findViewById(R.id.check); check.setRotation(-90); 

这工作对我来说,很好。 至于垂直下降的信件 – 我不知道。

在批准的答案中尝试了两个VerticalTextView类,他们工作得很好。

但是无论我尝试了什么,我都无法将这些VerticalTextView放置在包含布局的中间(一个RelativeLayout是RecyclerView的一个扩展项的一部分)。

FWIW,环顾四周后,我在GitHub上找到了yoog568的VerticalTextView类:

https://github.com/yoog568/VerticalTextView/blob/master/src/com/yoog/widget/VerticalTextView.java

我可以根据需要定位。

有一些小事情需要注意。

选择旋转或路径方式时,取决于字符集。 例如,如果目标字符集是英文的,并且期望的效果看起来像,

 a b c d 

您可以通过逐个绘制每个字符,不需要旋转或路径来获得此效果。

在这里输入图像描述

你可能需要旋转或路径来获得这种效果。

棘手的部分是当你尝试呈现像蒙古这样的字符集。 字体中的字形需要旋转90度,所以drawTextOnPath()将是一个很好的选择。

在Pointer Null的答案之后,我可以通过修改onDraw方法来水平居中文本:

 @Override protected void onDraw(Canvas canvas){ TextPaint textPaint = getPaint(); textPaint.setColor(getCurrentTextColor()); textPaint.drawableState = getDrawableState(); canvas.save(); if(topDown){ canvas.translate(getWidth()/2, 0); canvas.rotate(90); }else{ TextView temp = new TextView(getContext()); temp.setText(this.getText().toString()); temp.setTypeface(this.getTypeface()); temp.measure(0, 0); canvas.rotate(-90); int max = -1 * ((getWidth() - temp.getMeasuredHeight())/2); canvas.translate(canvas.getClipBounds().left, canvas.getClipBounds().top - max); } canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop()); getLayout().draw(canvas); canvas.restore(); } 

您可能需要添加TextView measuredWidth的一部分以将多线文本居中。

我喜欢@Kostmo的方法。 我修改了一下,因为我有一个问题 – 切断垂直旋转标签时,我把它的参数设置为WRAP_CONTENT 。 因此,一个文本是不完全可见的。

这是我解决它的方法:

 import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; import android.os.Build; import android.text.TextPaint; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class VerticalLabelView extends View { private final String LOG_TAG = "VerticalLabelView"; private int _ascent = 0; private int _leftPadding = 0; private int _topPadding = 0; private int _rightPadding = 0; private int _bottomPadding = 0; private int _textSize = 0; private int _measuredWidth; private int _measuredHeight; private Rect _textBounds; private TextPaint _textPaint; private String _text = ""; private TextView _tempView; private Typeface _typeface = null; public VerticalLabelView(Context context) { super(context); initLabelView(); } public VerticalLabelView(Context context, AttributeSet attrs) { super(context, attrs); initLabelView(); } public VerticalLabelView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initLabelView(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public VerticalLabelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initLabelView(); } private final void initLabelView() { this._textBounds = new Rect(); this._textPaint = new TextPaint(); this._textPaint.setAntiAlias(true); this._textPaint.setTextAlign(Paint.Align.CENTER); } public void setText(String text) { this._text = text; requestLayout(); invalidate(); } public void setPadding(int padding) { setPadding(padding, padding, padding, padding); } public void setPadding(int left, int top, int right, int bottom) { this._leftPadding = left; this._topPadding = top; this._rightPadding = right; this._bottomPadding = bottom; requestLayout(); invalidate(); } public void setTextSize(int size) { this._textSize = size; this._textPaint.setTextSize(size); requestLayout(); invalidate(); } public void setTextColor(int color) { this._textPaint.setColor(color); invalidate(); } public void setTypeFace(Typeface typeface) { this._typeface = typeface; this._textPaint.setTypeface(typeface); requestLayout(); invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { try { this._textPaint.getTextBounds(this._text, 0, this._text.length(), this._textBounds); this._tempView = new TextView(getContext()); this._tempView.setPadding(this._leftPadding, this._topPadding, this._rightPadding, this._bottomPadding); this._tempView.setText(this._text); this._tempView.setTextSize(TypedValue.COMPLEX_UNIT_PX, this._textSize); this._tempView.setTypeface(this._typeface); this._tempView.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); this._measuredWidth = this._tempView.getMeasuredHeight(); this._measuredHeight = this._tempView.getMeasuredWidth(); this._ascent = this._textBounds.height() / 2 + this._measuredWidth / 2; setMeasuredDimension(this._measuredWidth, this._measuredHeight); } catch (Exception e) { setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); Log.e(LOG_TAG, Log.getStackTraceString(e)); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (!this._text.isEmpty()) { float textHorizontallyCenteredOriginX = this._measuredHeight / 2f; float textHorizontallyCenteredOriginY = this._ascent; canvas.translate(textHorizontallyCenteredOriginY, textHorizontallyCenteredOriginX); canvas.rotate(-90); canvas.drawText(this._text, 0, 0, this._textPaint); } } }