Вертикальный (повернутый) ярлык в Андроид
Мне нужно 2 способа отображения вертикальной метки в Android:
- горизонтальная метка повернута на 90 градусов против часовой стрелки (буквы сбоку)
- горизонтальный ярлык с буквами ОДИН под другим (как знак магазина)
нужно ли мне разрабатывать пользовательские виджеты для обоих случаев (один случай), могу ли я сделать TextView для визуализации таким образом, и что было бы хорошим способом сделать что-то подобное, если мне нужно полностью настроить?
8 ответов:
вот моя элегантная и простая вертикальная текстовая реализация, расширяющая 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+(b-t), t+(r-l)); } @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.
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
:<resources> <declare-styleable name="VerticalLabelView"> <attr name="text" format="string" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> </declare-styleable> </resources>
один из способов достижения этих целей будет:
- напишите свой собственный просмотр и переопределение onDraw (Canvas). вы можете нарисовать текст на холсте, а затем повернуть полотно.
- то же, что и 1. кроме этого времени используйте путь и рисовать текст с помощью drawTextOnPath(...)
check = (TextView)findViewById(R.id.check); check.setRotation(-90);
это работает для меня просто отлично. Что касается вертикально идущих вниз букв-я не знаю.
попробовал оба класса VerticalTextView в утвержденном ответе, и они работали достаточно хорошо.
но независимо от того, что я пытался, я не смог разместить эти VerticalTextViews в центре содержащего макета (RelativeLayout, который является частью элемента, раздутого для RecyclerView).
FWIW, осмотревшись, я нашел класс 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
вы можете получить этот эффект, рисуя каждый символ по одному, без поворота или пути нужны.
для получения этого эффекта может потребоваться поворот или путь.
самое сложное, когда вы попробуйте сделать кодировку такой как монгольская. глиф в шрифте должен быть повернут на 90 градусов, поэтому drawTextOnPath() будет хорошим кандидатом для использования.
после Указатель 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 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)
метод.