Question

J'ai besoin 2 façons de montrer l'étiquette verticale dans Android:

  1. étiquette horizontale tourné de 90 degrés vers la gauche (lettres sur le côté)
  2. étiquette horizontale avec des lettres une sous l'autre (comme un signe de magasin)

Ai-je besoin de développer des widgets personnalisés pour les deux cas (un cas), puis-je faire TextView de rendre cette façon, et ce serait une bonne façon de faire quelque chose comme ça si je dois aller complètement sur mesure?

Était-ce utile?

La solution

Voici mon simple et élégante mise en œuvre du texte vertical, l'extension TextView. Cela signifie que tous les styles standard de TextView peuvent être utilisés, car elle est prolongée 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);
   }
}

Par défaut, le texte est mis en rotation de haut en bas. Si vous définissez android:. Gravité = « bas », il est tiré de bas en haut

Techniquement, il fous TextView sous-jacente de penser que c'est une rotation normale (largeur échange / hauteur en quelques endroits), tout en dessinant une rotation. Il fonctionne très bien aussi quand il est utilisé dans une mise en page XML.

EDIT: affichant une autre version, ci-dessus a des problèmes avec des animations. Cette nouvelle version fonctionne mieux, mais perd quelques fonctionnalités de TextView, comme chapiteau et spécialités similaires.

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

EDIT Version Kotlin:

import android.content.Context
import android.graphics.Canvas
import android.text.BoringLayout
import android.text.Layout
import android.text.TextUtils.TruncateAt
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.graphics.withSave

class VerticalTextView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs) {
    private val topDown = gravity.let { g ->
        !(Gravity.isVertical(g) && g.and(Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM)
    }
    private val metrics = BoringLayout.Metrics()
    private var padLeft = 0
    private var padTop = 0

    private var layout1: Layout? = null

    override fun setText(text: CharSequence, type: BufferType) {
        super.setText(text, type)
        layout1 = null
    }

    private fun makeLayout(): Layout {
        if (layout1 == null) {
            metrics.width = height
            paint.color = currentTextColor
            paint.drawableState = drawableState
            layout1 = BoringLayout.make(text, paint, metrics.width, Layout.Alignment.ALIGN_NORMAL, 2f, 0f, metrics, false, TruncateAt.END, height - compoundPaddingLeft - compoundPaddingRight)
            padLeft = compoundPaddingLeft
            padTop = extendedPaddingTop
        }
        return layout1!!
    }

    override fun onDraw(c: Canvas) {
        //      c.drawColor(0xffffff80); // TEST
        if (layout == null)
            return
        c.withSave {
            if (topDown) {
                val fm = paint.fontMetrics
                translate(textSize - (fm.bottom + fm.descent), 0f)
                rotate(90f)
            } else {
                translate(textSize, height.toFloat())
                rotate(-90f)
            }
            translate(padLeft.toFloat(), padTop.toFloat())
            makeLayout().draw(this)
        }
    }
}

Autres conseils

Je mis en place ce projet pour mon ChartDroid . Créer 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);
    }
}

Et dans 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>

Une façon d'atteindre ces objectifs serait:

  1. Rédigez votre propre href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" personnalisée et passer outre onDraw (Canvas). Vous pouvez dessiner le texte sur la toile puis faites pivoter la toile.
  2. Identique à 1. sauf que cette fois utiliser un Chemin de href="http://developer.android.com/reference/android/graphics/Path.html" dessiner le texte en utilisant drawTextOnPath (...)

Essayé les deux classes VerticalTextView dans la réponse approuvée, et ils ont travaillé assez bien.

Mais peu importe ce que j'ai essayé, je ne pouvais pas placer ces VerticalTextViews au centre de la mise en page contenant (un RelativeLayout qui fait partie d'un élément gonflé pour une RecyclerView).

FWIW, après avoir regardé autour, je trouve la classe VerticalTextView de yoog568 sur GitHub:

https://github.com/ yoog568 / VerticalTextView / blob / maître / src / com / yoog / widget / VerticalTextView.java

qui je pus position souhaitée. Vous devez également inclure la définition des attributs suivants dans votre projet:

https://github.com/yoog568/VerticalTextView/ blob / maître / rES / valeurs / attr.xml

check = (TextView)findViewById(R.id.check);
check.setRotation(-90);

Cela a fonctionné pour moi, très bien. En ce qui concerne les choses verticalement vers le bas des lettres - Je ne sais »

.

Il y a besoin d'être attention quelques petites choses sur.

Cela dépend de la charset lors du choix de la rotation ou les voies de chemin. par exemple, si le jeu de caractères cible est l'anglais comme, et l'effet attendu ressemble,

a
b
c
d

vous pouvez obtenir cet effet en dessinant chaque caractère un par un, ou le chemin ne rotate nécessaire.

entrer image description ici

vous devrez peut-être tourner ou le chemin pour obtenir cet effet.

la partie la plus délicate est lorsque vous essayez de rendre charset tels que la Mongolie. le glyphe dans le Typeface doivent être mis en rotation à 90 degrés, de sorte drawTextOnPath () sera un bon candidat à utiliser.

réponse de pointeur nul , j'ai pu centrer le texte horizontalement en modifiant la onDraw méthode de cette façon:

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

Vous devrez peut-être ajouter une partie de la TextView measuredWidth pour centrer un texte multilined.

Vous pouvez simplement ajouter à votre TextView ou autre Voir valeur de rotation xml. Ceci est la meilleure façon pour moi et bon fonctionnement.

<LinearLayout
    android:rotation="-90"
    android:layout_below="@id/image_view_qr_code"
    android:layout_above="@+id/text_view_savva_club"
    android:layout_marginTop="20dp"
    android:gravity="bottom"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

   <TextView
       android:textColor="@color/colorPrimary"
       android:layout_marginStart="40dp"
       android:textSize="20sp"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Дмитриевский Дмитрий Дмитриевич"
       android:maxLines="2"
       android:id="@+id/vertical_text_view_name"/>
    <TextView
        android:textColor="#B32B2A29"
        android:layout_marginStart="40dp"
        android:layout_marginTop="15dp"
        android:textSize="16sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/vertical_text_view_phone"
        android:text="+38 (000) 000-00-00"/>

</LinearLayout>

J'ai aimé @ l'approche de kostmo. Je l'ai modifié un peu, parce que j'avais un problème - couper l'étiquette tournée verticalement lorsque je mets ses params comme WRAP_CONTENT. Ainsi, un texte n'a pas été 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.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);
        }
    }
}

Si vous voulez avoir un texte de haut à utiliser vers le bas, puis méthode topToDown(true).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top