Вертикальная (повернутая) метка в Android

Мне нужно два способа показа вертикальной метки в Android:

  1. Горизонтальная метка повернулась на 90 gradleусов против часовой стрелки (буквы сбоку)
  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); } } 

По умолчанию повернутый текст сверху вниз. Если вы установите андроид: gravity = “bottom”, то он будет рисоваться снизу вверх.

Технически это обманывает TextView, чтобы думать, что это нормальный поворот (ширина / высота подкачки в нескольких местах), при этом он поворачивается. Он отлично работает и при использовании в XML-макете.

EDIT: публикация другой версии, выше, имеет проблемы с анимацией. Эта новая версия работает лучше, но теряет некоторые функции TextView, такие как marquee и аналогичные специальности.

 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 :

        

Один из способов их достижения:

  1. Создайте собственное собственное представление и переопределите onDraw (Canvas). Вы можете нарисовать текст на canvasе, а затем повернуть canvas.
  2. То же, что и 1. кроме этого времени используйте Path и нарисуйте текст, используя drawTextOnPath (…)
 check = (TextView)findViewById(R.id.check); check.setRotation(-90); 

Это сработало для меня, отлично. Что касается вертикально идущих букв – я не знаю.

Пробовал оба classа VerticalTextView в одобренном ответе, и они работали достаточно хорошо.

Но независимо от того, что я пробовал, я не смог разместить эти VerticalTextViews в центре содержащего макета (RelativeLayout, который является частью элемента, накаченного для RecyclerView).

FWIW, после осмотра, я нашел class VerticalTextView yoog568 в GitHub:

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

который я смог по желанию. Вы также должны включить в свой проект следующее определение атрибутов:

https://github.com/yoog568/VerticalTextView/blob/master/res/values/attr.xml

На некоторые мелочи нужно обратить внимание.

Это зависит от кодировки при выборе поворота или путей пути. например, если целевая кодировка похожа на английский, и ожидаемый эффект выглядит следующим образом:

 a b c d 

вы можете получить этот эффект, рисуя каждый символ по одному, не поворачиваясь и не направляясь.

введите описание изображения здесь

вам может потребоваться поворот или путь, чтобы получить этот эффект.

сложная часть – это когда вы пытаетесь сделать такую ​​кодировку, как монгольский. глиф в Typeface должен быть повернут на 90 gradleусов, поэтому drawTextOnPath () будет хорошим кандидатом на использование.

Следуя ответу указателя Null , я смог onDraw текст по горизонтали, модифицируя метод 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 final int DEFAULT_TEXT_SIZE = 30; 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; private boolean _topToDown = false; 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); this._textPaint.setTextSize(DEFAULT_TEXT_SIZE); this._textSize = DEFAULT_TEXT_SIZE; } public void setText(String text) { this._text = text; requestLayout(); invalidate(); } public void topToDown(boolean topToDown) { this._topToDown = topToDown; } 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); float rotateDegree = -90; float y = 0; if (this._topToDown) { rotateDegree = 90; y = this._measuredWidth / 2; } canvas.rotate(rotateDegree); canvas.drawText(this._text, 0, y, this._textPaint); } } } 

Если вы хотите иметь текст сверху вниз, используйте topToDown(true) .

  • Могу ли я обернуть каждую строку многострочного текста в промежутке?
  • Форматирование текста в ярлыке WinForm
  • Как эффективно анализировать CSV-файл в Perl?
  • Можно ли изменить цвет текста в строке на несколько цветов в Java?
  • Сортировка текстового файла по длине строки, включая пробелы
  • Как печатать цвет в консоли с помощью System.out.println?
  • Извлечение текста OpenCV
  • Чтобы нарисовать подчеркивание под TextView в Android
  • Java отрицательный int для шестнадцатеричного и обратного сбоев
  • Тайм-аут в async / wait
  • Хороший цвет текста переднего плана для заданного цвета фона
  • Давайте будем гением компьютера.