Etiquette verticale (pivotée) sous Android

J’ai besoin de 2 manières de montrer l’étiquette verticale dans Android:

  1. Étiquette horizontale tournée de 90 degrés dans le sens inverse des aiguilles d’une montre (lettres sur le côté)
  2. Étiquette horizontale avec des lettres l’une sous l’autre (comme un signe de magasin)

Dois-je développer des widgets personnalisés pour les deux cas (un cas), puis-je créer TextView de cette manière, et quel serait le bon moyen de faire quelque chose comme ça si je devais aller complètement sur mesure?

Voici mon implémentation de texte verticale élégante et simple, étendant TextView. Cela signifie que tous les styles standard de TextView peuvent être utilisés car TextView est étendu.

public class VerticalTextView extends TextView{ final boolean topDown; public VerticalTextView(Context context, AtsortingbuteSet 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); } } 

Par défaut, le texte pivoté est de haut en bas. Si vous définissez Android: gravity = “bottom”, il est dessiné de bas en haut.

Techniquement, il est difficile de penser que TextView sous-tend la rotation normale (permuter la largeur / hauteur dans quelques endroits), tout en le faisant pivoter. Cela fonctionne très bien aussi quand il est utilisé dans une mise en page XML.

EDIT: poster une autre version, ci-dessus a des problèmes avec les animations. Cette nouvelle version fonctionne mieux, mais perd certaines fonctionnalités de TextView, telles que les marques de fabrique et les spécialités similaires.

 public class VerticalTextView extends TextView{ final boolean topDown; public VerticalTextView(Context context, AtsortingbuteSet 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(); } } 

Je l’ai implémenté pour mon projet ChartDroid . Créez VerticalLabelView.java :

 public class VerticalLabelView extends View { private TextPaint mTextPaint; private Ssortingng 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, AtsortingbuteSet attrs) { super(context, attrs); initLabelView(); TypedArray a = context.obtainStyledAtsortingbutes(attrs, R.styleable.VerticalLabelView); CharSequence s = a.getSsortingng(R.styleable.VerticalLabelView_text); if (s != null) setText(s.toSsortingng()); 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(Ssortingng 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); } } 

Et dans attrs.xml :

        

Un moyen d’y parvenir serait:

  1. Rédigez votre propre vue personnalisée et remplacez onDraw (Canvas). Vous pouvez dessiner le texte sur le canevas, puis faire pivoter le canevas.
  2. Identique à 1. sauf que cette fois, utilisez un chemin et dessinez un texte en utilisant drawTextOnPath (…)
 check = (TextView)findViewById(R.id.check); check.setRotation(-90); 

Cela a fonctionné pour moi, très bien. En ce qui concerne les lettres descendant verticalement – je ne sais pas.

Essayé les deux classes de VerticalTextView dans la réponse approuvée, et ils ont fonctionné raisonnablement bien.

Mais peu importe ce que j’ai essayé, je n’ai pas pu positionner ces VerticalTextViews au centre de la présentation contenant (un RelativeLayout qui fait partie d’un élément gonflé pour un RecyclerView).

FWIW, après avoir regardé autour de moi, j’ai trouvé la classe VerticalTextView de yoog568 sur GitHub:

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

que j’ai pu positionner comme désiré. Vous devez également inclure la définition d’atsortingbuts suivante dans votre projet:

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

Il y a quelques petites choses à faire attention.

Cela dépend du jeu de caractères lors du choix de la rotation ou du chemin. Par exemple, si le jeu de caractères cible est l’anglais, et que l’effet attendu se présente,

 a b c d 

Vous pouvez obtenir cet effet en dessinant chaque caractère un par un, sans rotation ni chemin requirejs.

entrer la description de l'image ici

vous devrez peut-être faire pivoter ou suivre le chemin pour obtenir cet effet.

La partie la plus délicate est lorsque vous essayez de rendre des jeux de caractères tels que le mongol. Le glyphe du caractère doit être pivoté de 90 degrés, donc drawTextOnPath () sera un bon candidat à utiliser.

Suite à la réponse de Pointer Null , j’ai pu centrer le texte horizontalement en modifiant la méthode onDraw manière suivante:

 @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().toSsortingng()); 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(); } 

Vous devrez peut-être append une partie de TextView measuredWidth pour centrer un texte multiligne.

J’ai aimé l’approche de @ kostmo. Je l’ai un peu modifié, car j’avais un problème – couper l’étiquette tournée verticalement lorsque je définissais ses parameters comme WRAP_CONTENT . Ainsi, un texte n’était pas totalement visible.

Voici comment je l’ai résolu:

 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.AtsortingbuteSet; 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 Ssortingng 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 Ssortingng _text = ""; private TextView _tempView; private Typeface _typeface = null; private boolean _topToDown = false; public VerticalLabelView(Context context) { super(context); initLabelView(); } public VerticalLabelView(Context context, AtsortingbuteSet attrs) { super(context, attrs); initLabelView(); } public VerticalLabelView(Context context, AtsortingbuteSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initLabelView(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public VerticalLabelView(Context context, AtsortingbuteSet 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(Ssortingng 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.getStackTraceSsortingng(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); } } } 

Si vous souhaitez avoir un texte de haut en bas, utilisez la topToDown(true) .