Vertical (rotated) label in Android

后端 未结 10 1003
礼貌的吻别
礼貌的吻别 2020-11-22 08:04

I need 2 ways of showing vertical label in Android:

  1. Horizontal label turned 90 degrees counterclockwise (letters on the side)
  2. Horizontal label with l
相关标签:
10条回答
  • 2020-11-22 08:24

    You can just add to your TextView or other View xml rotation value. This is the easiest way and for me working correct.

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

    0 讨论(0)
  • 2020-11-22 08:26

    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)
    

    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(...)
    

    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)
    

    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 appearing like a store sign just insert newlines after each character, e.g. "N\nu\nt\ns" will be:

    0 讨论(0)
  • 2020-11-22 08:27
    check = (TextView)findViewById(R.id.check);
    check.setRotation(-90);
    

    This worked for me, just fine. As for the vertically going down letters - I dont' know.

    0 讨论(0)
  • 2020-11-22 08:30

    There are some minor things need to be pay attention on.

    It depends on the charset when choosing the rotate or the path ways. for example, if the target charset is English like, and the expected effect looks like,

    a
    b
    c
    d
    

    you can get this effect by drawing each character one by one, no rotate or path needed.

    enter image description here

    you may need rotate or path to get this effect.

    the tricky part is when you try to render charset such like Mongolian. the glyph in the Typeface need to be rotated 90 degree, so drawTextOnPath() will be a good candidate to use.

    0 讨论(0)
  • 2020-11-22 08:30

    Following Pointer Null's answer, I've been able to center the text horizontally by modifying the onDraw method this way:

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

    You might need to add a portion of the TextView measuredWidth to center a multilined text.

    0 讨论(0)
  • 2020-11-22 08:32

    I implemented this for my ChartDroid project. Create 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);
        }
    }

    And in 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>
    
    0 讨论(0)
提交回复
热议问题