开发者

Is it possible to write vertically in a textview in android?

Let's say 开发者_JAVA百科you have a normal TextView, with "Stackoverflow" written in it, Is it possible to rotate the TextView by -90°, to have the S at the bottom and the W at the top of the screen? Of course I could write my text as an image, rotate it and use it that way, but I am interested in the text right now. Thanks.


You can set your textview as you would normally do

for example:

 <TextView android:id="@+id/txtview"
    android:layout_height="fill_parent"
    android:layout_width="wrap_content" />

and write a function in your activity to

  • reverse the characters in your text
  • insert \n after every characters

and then set the text to the TextView.

If you dont want to insert the \n, you will have to set the size of android:layout_width and play with font size not to have 2 characters fitting on the same line and no truncation

Edit If I have understood you correctly, you can get what you want by using animation.

For example

Under res/anim/myanim.xml:

<rotate  xmlns:android="http://schemas.android.com/apk/res/android"
           android:fromDegrees="0" 
           android:toDegrees="-90"
           android:pivotX="50%"
           android:duration="0" />

You will have to play with this file to define where you want your text view to be placed.

In your activity:

  TextView t = (TextView)findViewById(R.id.txtview);
  String txt = "Stackoverflow";         
  t.setText(txt);

  RotateAnimation ranim = (RotateAnimation)AnimationUtils.loadAnimation(this, R.anim.myanim);
  ranim.setFillAfter(true); //For the textview to remain at the same place after the rotation
  t.setAnimation(ranim);


Worked for me:

public class VerticalTextView extends TextView {

    private int _width, _height;
    private final Rect _bounds = new Rect();

    public VerticalTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticalTextView(Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // vise versa
        _height = getMeasuredWidth();
        _width = getMeasuredHeight();
        setMeasuredDimension(_width, _height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();

        canvas.translate(_width, _height);
        canvas.rotate(-90);

        TextPaint paint = getPaint();
        paint.setColor(getTextColors().getDefaultColor());

        String text = text();

        paint.getTextBounds(text, 0, text.length(), _bounds);
        canvas.drawText(text, getCompoundPaddingLeft(), (_bounds.height() - _width) / 2, paint);

        canvas.restore();
    }

    private String text() {
        return super.getText().toString();
    }
}

xml:

<VerticalTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left|center_vertical"
            android:background="@color/feedback_background"
            android:padding="4dip"
            android:text="@string/feedback"
            android:textColor="@color/feedback_text_color"
            android:textSize="@dimen/text_xlarge" />


        <TextView
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:text="xyz"
            android:rotation="-90"
            android:gravity="fill_vertical"/>


Try this. It works fine for me. It can display one line of text vertically, but just one line. colors, size, paddings, margins and background all work fine.

public class VerticalTextView extends TextView {

    public VerticalTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticalTextView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        final ColorStateList csl = getTextColors();
        final int color = csl.getDefaultColor();
        final int paddingBottom = getPaddingBottom();
        final int paddingTop = getPaddingTop();
        final int viewWidth = getWidth();
        final int viewHeight = getHeight();
        final TextPaint paint = getPaint();
        paint.setColor(color);
        final float bottom = viewWidth * 9.0f / 11.0f;
        Path p = new Path();
        p.moveTo(bottom, viewHeight - paddingBottom - paddingTop);
        p.lineTo(bottom, paddingTop);
        canvas.drawTextOnPath(getText().toString(), p, 0, 0, paint);
    }
}


If you are using API 11 or later, you may try:

TextView t = (TextView) findViewById(R.id.txtview);
String txt = "Stackoverflow";         
t.setText(txt);
t.setRotation(90); // 90 degree rotation


I'll show for you guys my example of custom vertical button with the rotated TextView in it:

<!--Undo button-->
<LinearLayout
    android:id="@+id/undo_points_pr_a"
    android:layout_width="@dimen/zero_dp"
    android:gravity="center"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_weight="1"
    android:background="@color/timerUndoButton">

    <ImageView
        android:layout_width="@dimen/large"
        android:layout_height="@dimen/large"
        android:src="@drawable/undo_icon"
        android:rotation="-90"
        android:layout_marginBottom="@dimen/medium"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/undo"
        android:textSize="@dimen/small_medium_text"
        android:rotation="-90"/>

</LinearLayout>

And this is how it looks in Android Studio:

Is it possible to write vertically in a textview in android?

And of course you have to modify this code to make it works for you. (in attributes like android:layout_width, android:layout_height, etc.)


I provided a solution in another StackOverflow question. You can get vertical TextView by extending from View and overriding its onMeasure() and onDraw() methods. However, it will not support all TextView features, rather its main ones like padding, size, color and font.

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


I think the simplest answer to your question to write "Stackoverflow" vertically is to use an ordinary TextView, and since the text will wrap to the next line when narrowed, play around with the width of the TextView so there is one letter is on each line and if you need more space on the edge as a buffer increase the "padding" and/or "margin" of the TextView.


My initial approach to rendering vertical text inside a vertical LinearLayout was as follows (this is Kotlin, in Java use setRoatation etc.):

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

Is it possible to write vertically in a textview in android?

As you can see the problem is that the TextView goes vertically but still treats its width as if it were oriented horizontally! =/

Thus approach #2 consisted of additionally switching width and height manually to account for this:

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

Is it possible to write vertically in a textview in android?

This however resulted in the labels wrapping around to the next line (or being cropped if you setSingleLine) after the relatively short width. Again, this boils down to confusing x with y.

My approach #3 was thus to wrap the TextView in a RelativeLayout. The idea is to allow the TextView any width it wants by extending it far to the left and the right (here, 200 pixels in both directions). But then I give the RelativeLayout negative margins to ensure it is drawn as a narrow column. Here is my full code for this screenshot:

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)

Is it possible to write vertically in a textview in android?

As a general tip, this strategy of having a view "hold" another view has been really useful for me in positioning things in Android! For example, the info window below the ActionBar uses the same tactic!

For text starting at the bottom just rotate it by -90F instead of 90F degrees.


public class VerticalTextView extends AppCompatTextView {
    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();
    }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜