code: package io.virtualapp.widgets;
  
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  
  import io.virtualapp.R;
  
  public class LabelView extends View {
      private static final int DEFAULT_DEGREES = 45;
      private String mTextContent;
      private int mTextColor;
      private float mTextSize;
      private boolean mTextBold;
      private boolean mFillTriangle;
      private boolean mTextAllCaps;
      private int mBackgroundColor;
      private float mMinSize;
      private float mPadding;
      private int mGravity;
      private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      private Path mPath = new Path();
  
      public LabelView(Context context) {
          this(context, null);
      }
  
      public LabelView(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          obtainAttributes(context, attrs);
          mTextPaint.setTextAlign(Paint.Align.CENTER);
      }
  
      private void obtainAttributes(Context context, AttributeSet attrs) {
          TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LabelView);
          mTextContent = ta.getString(R.styleable.LabelView_lv_text);
          mTextColor = ta.getColor(R.styleable.LabelView_lv_text_color, Color.parseColor("#ffffff"));
          mTextSize = ta.getDimension(R.styleable.LabelView_lv_text_size, sp2px(11));
          mTextBold = ta.getBoolean(R.styleable.LabelView_lv_text_bold, true);
          mTextAllCaps = ta.getBoolean(R.styleable.LabelView_lv_text_all_caps, true);
          mFillTriangle = ta.getBoolean(R.styleable.LabelView_lv_fill_triangle, false);
          mBackgroundColor = ta.getColor(R.styleable.LabelView_lv_background_color, Color.parseColor("#FF4081"));
          mMinSize = ta.getDimension(R.styleable.LabelView_lv_min_size, mFillTriangle ? dp2px(35) : dp2px(50));
          mPadding = ta.getDimension(R.styleable.LabelView_lv_padding, dp2px(3.5f));
          mGravity = ta.getInt(R.styleable.LabelView_lv_gravity, Gravity.TOP | Gravity.LEFT);
          ta.recycle();
      }
  
      public String getText() {
          return mTextContent;
      }
  
      public void setText(String text) {
          mTextContent = text;
          invalidate();
      }
  
      public int getTextColor() {
          return mTextColor;
      }
  
      public void setTextColor(int textColor) {
          mTextColor = textColor;
          invalidate();
      }
  
      public float getTextSize() {
          return mTextSize;
      }
  
      public void setTextSize(float textSize) {
          mTextSize = sp2px(textSize);
          invalidate();
      }
  
      public boolean isTextBold() {
          return mTextBold;
      }
  
      public void setTextBold(boolean textBold) {
          mTextBold = textBold;
          invalidate();
      }
  
      public boolean isFillTriangle() {
          return mFillTriangle;
      }
  
      public void setFillTriangle(boolean fillTriangle) {
          mFillTriangle = fillTriangle;
          invalidate();
      }
  
      public boolean isTextAllCaps() {
          return mTextAllCaps;
      }
  
      public void setTextAllCaps(boolean textAllCaps) {
          mTextAllCaps = textAllCaps;
          invalidate();
      }
  
      public int getBgColor() {
          return mBackgroundColor;
      }
  
      public void setBgColor(int backgroundColor) {
          mBackgroundColor = backgroundColor;
          invalidate();
      }
  
      public float getMinSize() {
          return mMinSize;
      }
  
      public void setMinSize(float minSize) {
          mMinSize = dp2px(minSize);
          invalidate();
      }
  
      public float getPadding() {
          return mPadding;
      }
  
      public void setPadding(float padding) {
          mPadding = dp2px(padding);
          invalidate();
      }
  
      public int getGravity() {
          return mGravity;
      }
  
      /**
       * Gravity.TOP | Gravity.LEFT
       * Gravity.TOP | Gravity.RIGHT
       * Gravity.BOTTOM | Gravity.LEFT
       * Gravity.BOTTOM | Gravity.RIGHT
       */
      public void setGravity(int gravity) {
          mGravity = gravity;
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
          int size = getHeight();
  
          mTextPaint.setColor(mTextColor);
          mTextPaint.setTextSize(mTextSize);
          mTextPaint.setFakeBoldText(mTextBold);
          mBackgroundPaint.setColor(mBackgroundColor);
  
          float textHeight = mTextPaint.descent() - mTextPaint.ascent();
          if (mFillTriangle) {
              if (mGravity == (Gravity.TOP | Gravity.LEFT)) {
                  mPath.reset();
                  mPath.moveTo(0, 0);
                  mPath.lineTo(0, size);
                  mPath.lineTo(size, 0);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, true);
              } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {
                  mPath.reset();
                  mPath.moveTo(size, 0);
                  mPath.lineTo(0, 0);
                  mPath.lineTo(size, size);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawTextWhenFill(size, DEFAULT_DEGREES, canvas, true);
              } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {
                  mPath.reset();
                  mPath.moveTo(0, size);
                  mPath.lineTo(0, 0);
                  mPath.lineTo(size, size);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawTextWhenFill(size, DEFAULT_DEGREES, canvas, false);
              } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {
                  mPath.reset();
                  mPath.moveTo(size, size);
                  mPath.lineTo(0, size);
                  mPath.lineTo(size, 0);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawTextWhenFill(size, -DEFAULT_DEGREES, canvas, false);
              }
          } else {
              double delta = (textHeight + mPadding * 2) * Math.sqrt(2);
              if (mGravity == (Gravity.TOP | Gravity.LEFT)) {
                  mPath.reset();
                  mPath.moveTo(0, (float) (size - delta));
                  mPath.lineTo(0, size);
                  mPath.lineTo(size, 0);
                  mPath.lineTo((float) (size - delta), 0);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawText(size, -DEFAULT_DEGREES, canvas, textHeight, true);
              } else if (mGravity == (Gravity.TOP | Gravity.RIGHT)) {
                  mPath.reset();
                  mPath.moveTo(0, 0);
                  mPath.lineTo((float) delta, 0);
                  mPath.lineTo(size, (float) (size - delta));
                  mPath.lineTo(size, size);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawText(size, DEFAULT_DEGREES, canvas, textHeight, true);
              } else if (mGravity == (Gravity.BOTTOM | Gravity.LEFT)) {
                  mPath.reset();
                  mPath.moveTo(0, 0);
                  mPath.lineTo(0, (float) delta);
                  mPath.lineTo((float) (size - delta), size);
                  mPath.lineTo(size, size);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawText(size, DEFAULT_DEGREES, canvas, textHeight, false);
              } else if (mGravity == (Gravity.BOTTOM | Gravity.RIGHT)) {
                  mPath.reset();
                  mPath.moveTo(0, size);
                  mPath.lineTo((float) delta, size);
                  mPath.lineTo(size, (float) delta);
                  mPath.lineTo(size, 0);
                  mPath.close();
                  canvas.drawPath(mPath, mBackgroundPaint);
  
                  drawText(size, -DEFAULT_DEGREES, canvas, textHeight, false);
              }
          }
      }
  
      private void drawText(int size, float degrees, Canvas canvas, float textHeight, boolean isTop) {
          canvas.save();
          canvas.rotate(degrees, size / 2f, size / 2f);
          float delta = isTop ? -(textHeight + mPadding * 2) / 2 : (textHeight + mPadding * 2) / 2;
          float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;
          canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,
                  getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);
          canvas.restore();
      }
  
      private void drawTextWhenFill(int size, float degrees, Canvas canvas, boolean isTop) {
          canvas.save();
          canvas.rotate(degrees, size / 2f, size / 2f);
          float delta = isTop ? -size / 4 : size / 4;
          float textBaseY = size / 2 - (mTextPaint.descent() + mTextPaint.ascent()) / 2 + delta;
          canvas.drawText(mTextAllCaps ? mTextContent.toUpperCase() : mTextContent,
                  getPaddingLeft() + (size - getPaddingLeft() - getPaddingRight()) / 2, textBaseY, mTextPaint);
          canvas.restore();
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int measuredWidth = measureWidth(widthMeasureSpec);
          setMeasuredDimension(measuredWidth, measuredWidth);
      }
  
      /**
       * 确定View宽度大小
       */
      private int measureWidth(int widthMeasureSpec) {
          int result;
          int specMode = MeasureSpec.getMode(widthMeasureSpec);
          int specSize = MeasureSpec.getSize(widthMeasureSpec);
          if (specMode == MeasureSpec.EXACTLY) {//大小确定直接使用
              result = specSize;
          } else {
              int padding = getPaddingLeft() + getPaddingRight();
              mTextPaint.setColor(mTextColor);
              mTextPaint.setTextSize(mTextSize);
              float textWidth = mTextPaint.measureText(mTextContent + "");
              result = (int) ((padding + (int) textWidth) * Math.sqrt(2));
              //如果父视图的测量要求为AT_MOST,即限定了一个最大值,则再从系统建议值和自己计算值中去一个较小值
              if (specMode == MeasureSpec.AT_MOST) {
                  result = Math.min(result, specSize);
              }
  
              result = Math.max((int) mMinSize, result);
          }
  
          return result;
      }
  
      protected int dp2px(float dp) {
          final float scale = getResources().getDisplayMetrics().density;
          return (int) (dp * scale + 0.5f);
      }
  
      protected int sp2px(float sp) {
          final float scale = getResources().getDisplayMetrics().scaledDensity;
          return (int) (sp * scale + 0.5f);
      }
  }