• category: swing folder: CornerRibbonLabel title: JLabelの隅に斜めに回転した文字列とリボンと重ねて表示する tags: [JLabel, AffineTransform] author: aterai pubdate: 2020-08-03T14:31:02+09:00 description: JLabelの右上隅に斜め45度に回転した文字列とリボンと重ねて表示します。 image: https://drive.google.com/uc?id=1AbO4jLsaajRhg6M5GuBlsdZg6BIg3Abn hreflang:
       href: https://java-swing-tips.blogspot.com/2020/08/overlap-jlabel-with-ribbon-and-slanted.html
       lang: en

概要

JLabelの右上隅に斜め45度に回転した文字列とリボンと重ねて表示します。

サンプルコード

class BadgeLabel extends JLabel {
  private final Color ribbonColor = new Color(0xAA_FF_64_00, true);
  private final String ribbonText;

  protected BadgeLabel(Icon image) {
    super(image);
    this.ribbonText = null;
  }

  protected BadgeLabel(Icon image, String ribbonText) {
    super(image);
    this.ribbonText = ribbonText;
  }

  @Override public void updateUI() {
    super.updateUI();
    setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
    setVerticalAlignment(SwingConstants.CENTER);
    setVerticalTextPosition(SwingConstants.BOTTOM);
    setHorizontalAlignment(SwingConstants.CENTER);
    setHorizontalTextPosition(SwingConstants.CENTER);
  }

  @Override protected void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setPaint(Color.WHITE);
    g2.fill(getShape());
    super.paintComponent(g);

    if (ribbonText != null) {
      Dimension d = getSize();
      float fontSize = 10f;
      int cx = (d.width - (int) fontSize) / 2;
      double theta = Math.toRadians(45d);

      Font font = g2.getFont().deriveFont(fontSize);
      g2.setFont(font);
      FontRenderContext frc = new FontRenderContext(null, true, true);

      Shape ribbon = new Rectangle2D.Double(cx, -fontSize, d.width, fontSize);
      AffineTransform at = AffineTransform.getRotateInstance(theta, cx, 0);
      g2.setPaint(ribbonColor);
      g2.fill(at.createTransformedShape(ribbon));

      TextLayout tl = new TextLayout(ribbonText, font, frc);
      g2.setPaint(Color.WHITE);
      Rectangle2D r = tl.getOutline(null).getBounds2D();
      double dx = cx + (d.width - cx) / Math.sqrt(2d) - r.getWidth() / 2d;
      double dy = fontSize / 2d + r.getY();
      AffineTransform tx = AffineTransform.getTranslateInstance(dx, dy);
      Shape s = tl.getOutline(tx);
      g2.fill(at.createTransformedShape(s));
    }
    g2.dispose();
  }

  @Override public boolean isOpaque() {
    return false;
  }

  protected Shape getShape() {
    Dimension d = getSize();
    double r = d.width / 2d;
    return new RoundRectangle2D.Double(
        0d, 0d, d.width - 1d, d.height - 1d, r, r);
  }
}
View in GitHub: Java, Kotlin

解説

  • JLabel
    • JLabel#isOpaque()をオーバーライドして透明化
    • JLabel#paintComponent(...)をオーバーライドして背景用のラウンド矩形、JLabelの元アイコン、右上隅のリボン、リボン文字列の順に描画
  • リボン
    • JLabelx軸中央付近にリボンの左下が配置されるように矩形を作成
    • リボン矩形の左下を原点にMath.toRadians(45d)回転
  • リボン文字列
    • TextLayout.getOutline()メソッドで文字列をShapeに変換
    • 文字列のShapeをその左下を原点にMath.toRadians(45d)回転

参考リンク

コメント