Android中的垂直(旋转)标签

Bos*_*one 106 android label vertical-alignment textview

我需要2种在Android中显示垂直标签的方法:

  1. 水平标签逆时针旋转90度(侧面字母)
  2. 水平标签与字母一个在另一个下面(如商店标志)

我是否需要为这两种情况开发自定义小部件(一种情况),我可以使TextView以这种方式呈现,如果我需要完全自定义,那么这样做的好方法是什么?

Poi*_*ull 234

这是我优雅而简单的垂直文本实现,扩展了TextView.这意味着可以使用TextView的所有标准样式,因为它是扩展的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);
   }
}
Run Code Online (Sandbox Code Playgroud)

默认情况下,旋转的文本是从上到下.如果你设置android:gravity ="bottom",那么它是从下到上绘制的.

从技术上讲,它欺骗底层TextView认为它是正常旋转(在几个地方交换宽度/高度),同时绘制它旋转.在xml布局中使用时也可以正常工作.

编辑: 发布另一个版本,上面有动画问题.这个新版本效果更好,但丢失了一些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 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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这有多线和滚动条的问题. (4认同)
  • 效果很好,就我而言,我必须扩展 **android.support.v7.widget.AppCompatTextView** 而不是 **TextView** 才能使我的样式属性正常工作 (3认同)
  • 您的解决方案禁用`TextView`中的链接.实际上链接带有下划线,但没有响应点击. (2认同)
  • @ blackst0ne而不是<TextView>标签,使用自定义视图标记:<com.YOUR_PACKAGE_NAME.VerticalTextView> (2认同)
  • androidx java.lang.IllegalArgumentException 出现此异常:无效 Region.Op - 只允许相交和差值 VerticalTextView.java:44 (2认同)
  • 设置 android:rotation="270" 后,它可以工作,但它也占用了水平视图的空间 (2认同)
  • 这是行不通的。文本未完整显示。显示椭圆 (2认同)

kos*_*tmo 31

我为ChartDroid项目实现了这个.创建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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并在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>
Run Code Online (Sandbox Code Playgroud)


Pra*_*ast 9

实现这些目标的一种方法是:

  1. 编写自己的自定义视图并覆盖onDraw(Canvas).您可以在画布上绘制文本,然后旋转画布.
  2. 与1.相同除非此时使用路径并使用drawTextOnPath(...)绘制文本


alb*_*aun 8

在批准的答案中尝试了两个VerticalTextView类,并且它们运行得相当好.

但无论我尝试什么,我都无法将那些VerticalTextView放置在包含布局的中心(RelativeLayout,它是为RecyclerView充气的项目的一部分).

FWIW,环顾四周后,我在GitHub上找到了yoog568的VerticalTextView类:

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

我可以根据需要定位.您还需要在项目中包含以下属性定义:

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