Label vertikal (diputar) di Android

110

Saya membutuhkan 2 cara untuk menampilkan label vertikal di Android:

  1. Label horizontal diputar 90 derajat berlawanan arah jarum jam (huruf di samping)
  2. Label horizontal dengan huruf satu di bawah yang lain (seperti tanda toko)

Apakah saya perlu mengembangkan widget khusus untuk kedua kasus (satu kasus), dapatkah saya membuat TextView untuk dirender seperti itu, dan cara apa yang baik untuk melakukan hal seperti itu jika saya harus sepenuhnya menyesuaikan?

Bostone
sumber
Hal ini dimungkinkan untuk dilakukan dalam XML sejak API 11 (Android 3.0). stackoverflow.com/questions/3774770/…
chobok

Jawaban:

239

Ini adalah implementasi teks vertikal saya yang elegan dan sederhana, memperluas TextView. Ini berarti bahwa semua gaya standar TextView dapat digunakan, karena ini merupakan TextView yang diperluas.

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

Secara default, teks yang diputar dari atas ke bawah. Jika Anda menyetel android: gravity = "bottom", maka itu digambar dari bawah ke atas.

Secara teknis, itu menipu TextView yang mendasari untuk berpikir bahwa itu adalah rotasi normal (menukar lebar / tinggi di beberapa tempat), sambil menggambarnya diputar. Ini berfungsi dengan baik juga saat digunakan dalam tata letak xml.

EDIT: memposting versi lain, di atas bermasalah dengan animasi. Versi baru ini bekerja lebih baik, tetapi kehilangan beberapa fitur TextView, seperti marquee dan spesialisasi serupa.

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 versi 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)
        }
    }
}
Pointer Null
sumber
2
Solusi Anda menonaktifkan tautan di TextView. Sebenarnya tautan digarisbawahi, tetapi tidak merespons saat diklik.
Gurnetko
5
ini memiliki masalah dengan multiline dan scrollbar.
Cynichniy Bandera
1
maaf untuk pertanyaan dasar saya, Jika saya mendapat TextView (dalam file XML) dan saya ingin memutarnya, bagaimana saya harus memanggil kelas VerticalTextView?
blackst0ne
2
@ blackst0ne alih-alih tag <TextView>, gunakan tag tampilan khusus: <com.YOUR_PACKAGE_NAME.VerticalTextView>
Daiwik Daarun
1
berfungsi dengan baik, dalam kasus saya, saya harus memperluas android.support.v7.widget.AppCompatTextView alih-alih TextView untuk membuat atribut gaya saya berfungsi
Louis
32

Saya menerapkan ini untuk proyek ChartDroid saya . Buat 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);
    }
}

Dan di 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>
kostmo
sumber
Sangat berguna. Berikut ini tautan ke versi utama kode proyek Anda.google.com/p/chartdroid/source/browse//trunk/core/…
rds
Tidak berguna, teks tidak ditampilkan sepenuhnya .. terpotong dari akhir dan awal.
Siddarth G
Bekerja di atas android versi 28 juga
Ajeet Choudhary
Versi Anda adalah opsi terbaik untuk proyek saya tetapi setTextColor tidak berfungsi, juga saya ingin menerapkan gaya (latar belakang dan fontFamily) apakah mungkin untuk melakukannya?
Pablo R.20
9

Salah satu cara untuk mencapainya adalah:

  1. Tulis tampilan kustom Anda sendiri dan timpa onDraw (Canvas).Anda dapat menggambar teks di kanvas dan kemudian memutar kanvas.
  2. Sama seperti 1. kecuali kali ini gunakan Path dan gambar teks menggunakan drawTextOnPath (...)
Prashast
sumber
Jadi sebelum saya pergi ke rute itu (saya melihat metode onDraw untuk TextView - sangat besar) saya perhatikan bahwa seluruh perbedaan antara TextView dan Tombol perluasan adalah ID gaya internal (com.android.internal.R.attr.buttonStyle) Apakah mungkin cukup mendefinisikan gaya kustom dan memperluas TextView mirip dengan Button? Saya menduga jawabannya adalah tidak karena mungkin tidak mungkin untuk
mengubah
Apakah pendekatan ini benar-benar berhasil? Saya belum berhasil dan orang ini juga tidak ... osdir.com/ml/Android-Developers/2009-11/msg02810.html
nategood
1
2. drawTextOnPath () menggambar teks seperti itu diputar, sama seperti 1. Untuk menulis huruf satu di bawah yang lain, baik gunakan \nsetelah setiap karakter atau jika Anda memiliki font dengan lebar tetap, batasi lebar TextView agar pas hanya dengan satu karakter.
blub
8

Mencoba kedua kelas VerticalTextView dalam jawaban yang disetujui, dan mereka bekerja dengan cukup baik.

Tapi apa pun yang saya coba, saya tidak dapat memposisikan VerticalTextView tersebut di tengah layout yang memuatnya (RelativeLayout yang merupakan bagian dari item yang digelembungkan untuk RecyclerView).

FWIW, setelah melihat-lihat, saya menemukan kelas VerticalTextView yoog568 di GitHub:

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

yang dapat saya posisikan sesuai keinginan. Anda juga perlu menyertakan definisi atribut berikut dalam proyek Anda:

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

albert c braun
sumber
1
Saya menemukan penerapan ini sangat mudah!
Hashim Akhtar
4
check = (TextView)findViewById(R.id.check);
check.setRotation(-90);

Ini berhasil untuk saya, baik-baik saja. Adapun huruf vertikal turun - saya tidak 'tahu.

ERJAN
sumber
7
tetapi bahkan mengambil ruang secara horizontal, dan memutarnya secara vertikal
Prabs
3

Ada beberapa hal kecil yang perlu diperhatikan.

Itu tergantung pada charset saat memilih cara memutar atau jalur. Misalnya, jika kumpulan karakter target adalah bahasa Inggris, dan efek yang diharapkan terlihat seperti,

a
b
c
d

Anda bisa mendapatkan efek ini dengan menggambar setiap karakter satu per satu, tidak perlu memutar atau jalur.

masukkan deskripsi gambar di sini

Anda mungkin perlu memutar atau jalur untuk mendapatkan efek ini.

bagian yang sulit adalah ketika Anda mencoba membuat charset seperti Mongolian. mesin terbang di Typeface perlu diputar 90 derajat, jadi drawTextOnPath () akan menjadi kandidat yang baik untuk digunakan.

Angin barat
sumber
Bagaimana itu bisa dilakukan dengan cara lain dari leftSide ke RightSide
Rakesh
3
textview.setTextDirection (View.TEXT_DIRECTION_RTL) atau textview.setTextDirection (View.TEXT_DIRECTION_ANY_RTL) mungkin bekerja di atas API level 17. Anda dapat mengujinya.
Zephyr
cerdas dan sederhana
Abdulrahman Abdelkader
1

Mengikuti jawaban Pointer Null , saya dapat memusatkan teks secara horizontal dengan memodifikasi onDrawmetode seperti ini:

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

Anda mungkin perlu menambahkan sebagian TextView measureWidth untuk memusatkan teks multilined.

dequec64.dll
sumber
1

Anda bisa menambahkan ke TextView atau nilai rotasi xml View lainnya. Ini adalah cara termudah dan bagi saya bekerja dengan benar.

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

Hasil

Marina Budkovets
sumber
0

Pendekatan awal saya untuk merender teks vertikal di dalam LinearLayout vertikal adalah sebagai berikut (ini Kotlin, dalam penggunaan Java, setRoatationdll.):

val tv = TextView(context)
tv.gravity = Gravity.CENTER
tv.rotation = 90F
tv.height = calcHeight(...)
linearLabels.addView(tv)

pendekatan # 1

Seperti yang Anda lihat, masalahnya adalah TextView berjalan secara vertikal tetapi masih memperlakukan lebarnya seolah-olah diorientasikan secara horizontal! = /

Jadi pendekatan # 2 terdiri dari tambahan lebar dan tinggi secara manual untuk memperhitungkan ini:

tv.measure(0, 0)
// tv.setSingleLine()
tv.width = tv.measuredHeight
tv.height = calcHeight(...)

pendekatan # 2

Namun ini mengakibatkan label membungkus ke baris berikutnya (atau dipotong jika Anda setSingleLine ) setelah lebar yang relatif pendek. Sekali lagi, ini bermuara pada membingungkan x dengan y.

Pendekatan saya # 3 dengan demikian adalah membungkus TextView dalam RelativeLayout. Idenya adalah untuk mengizinkan TextView dengan lebar apa pun yang diinginkan dengan memperluasnya jauh ke kiri dan kanan (di sini, 200 piksel di kedua arah). Tapi kemudian saya memberikan margin negatif RelativeLayout untuk memastikannya digambar sebagai kolom sempit. Ini kode lengkap saya untuk tangkapan layar ini:

val tv = TextView(context)
tv.text = getLabel(...)
tv.gravity = Gravity.CENTER
tv.rotation = 90F

tv.measure(0, 0)
tv.width = tv.measuredHeight + 400  // 400 IQ
tv.height = calcHeight(...)

val tvHolder = RelativeLayout(context)
val lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
    LinearLayout.LayoutParams.WRAP_CONTENT)
lp.setMargins(-200, 0, -200, 0)
tvHolder.layoutParams = lp
tvHolder.addView(tv)
linearLabels.addView(tvHolder)

val iv = ImageView(context)
iv.setImageResource(R.drawable.divider)
linearLabels.addView(iv)

pendekatan # 3

Sebagai tip umum, strategi memiliki tampilan "tahan" tampilan lain ini sangat berguna bagi saya dalam memposisikan sesuatu di Android! Misalnya, jendela info di bawah ActionBar menggunakan taktik yang sama!

Untuk teks yang muncul seperti tanda toko cukup masukkan baris baru setelah setiap karakter, misalnya "N\nu\nt\ns"akan menjadi:

contoh tanda toko

xjcl
sumber
-1

Saya menyukai pendekatan @ kostmo. Saya memodifikasinya sedikit, karena saya mengalami masalah - memotong label yang diputar secara vertikal ketika saya menetapkan parameternya sebagai WRAP_CONTENT. Jadi, teks tidak sepenuhnya terlihat.

Beginilah cara saya menyelesaikannya:

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

Jika Anda ingin memiliki teks dari atas ke bawah, gunakan topToDown(true)metode.

Ayaz Alifov
sumber