重绘 JLabel 以获得渐变绘制的文本

Redrawing a JLabel to get a Gradient Painted Text

我正在尝试为从单词顶部到底部而不是从左到右的文本创建渐变绘画。我实际上是在这个 link here 的帮助下实现的。他们采用文字的形状,并将其绘制在面板上。我只是简单地编辑了他们的代码,并能够应用我正在寻找的效果。这是我将他们的绘画方法编辑为(其中 s 是一个形状):

@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D) g.create();
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  g2.translate(100, 150);
  Rectangle2D r = s.getBounds2D();
  int x = (int) r.getX();
  int y = (int) r.getY();
  int h = (int) r.getHeight();
  GradientPaint gp = new GradientPaint(x, y, Color.MAGENTA, x, h, Color.ORANGE);
  g2.setPaint(gp);
  g2.fill(s);
}

这行得通,但是这种方法重写了 JPanel 的 paintComponent 方法。我正在尝试通过扩展 JLabel 的新 GradientLabel Class 重新创建它。我遇到的问题是 g2d.fill(s) 方法在标签上方某处绘制形状,似乎遥不可及。我不明白为什么要这样做。也许它来自铸造 Graphics2D g.create();?我不得不添加 g2.translate(x,y) 方法将形状下拉到可见位置。

我想我有 2 个问题。

  1. 为什么 g2.fill(s) 不在 JLabel 超级方法绘制的文本上绘制形状?这可能是因为我的布局管理器?

  2. 我应该采用这种方法吗?有没有更简单的方法将垂直绘画渐变应用于文本?

这是用于测试的最小代码:

public class test extends JFrame {

private JPanel contentPane;

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                test frame = new test();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}


public test() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 516, 360);
    contentPane = new JPanel();
    contentPane.setBorder(null);
    setContentPane(contentPane);
    GridBagLayout gbl_contentPane = new GridBagLayout();
    gbl_contentPane.columnWidths = new int[]{0, 0};
    gbl_contentPane.rowHeights = new int[]{0, 100, 0};
    gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
    gbl_contentPane.rowWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
    contentPane.setLayout(gbl_contentPane);
    
    Component verticalStrut = Box.createVerticalStrut(20);
    GridBagConstraints gbc_verticalStrut = new GridBagConstraints();
    gbc_verticalStrut.insets = new Insets(0, 0, 5, 0);
    gbc_verticalStrut.gridx = 0;
    gbc_verticalStrut.gridy = 0;
    contentPane.add(verticalStrut, gbc_verticalStrut);

    GradientLabel lblTest = new GradientLabel("TEST");
    lblTest.setHorizontalAlignment(SwingConstants.CENTER);
    lblTest.setGradientColors(Color.GREEN, Color.WHITE);
    lblTest.setFont(new Font("Tahoma", Font.PLAIN, 70));
    GridBagConstraints gbc_lblTest = new GridBagConstraints();
    gbc_lblTest.fill = GridBagConstraints.BOTH;
    gbc_lblTest.gridx = 0;
    gbc_lblTest.gridy = 1;
    contentPane.add(lblTest, gbc_lblTest);
}

public class GradientLabel extends JLabel {

    private Color c1;
    private Color c2;

    public GradientLabel(String text) {
        setText(text);
        this.setOpaque(false);
    }

    public void setGradientColors(Color c1, Color c2) {
        this.c1 = c1;
        this.c2 = c2;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Font f = getFont();
        GlyphVector v = f.createGlyphVector(getFontMetrics(f).getFontRenderContext(), getText());
        Shape s = v.getOutline();
        Rectangle2D r = s.getBounds2D();
        int x = (int) r.getX();
        int y = (int) r.getY();
        int h = (int) r.getHeight();
        int w = (int) r.getWidth();
    
        //without this the shape is drawn almost out of view
        g2d.translate(x, h);
        g2d.drawRect(x, y, w, h);
        //for some reason using only h as the second y doesn't show much of the second color. 
        //Subtracting 60 arbitrarily showed more of the second color in the gradient. 
        //Bonus points for an explanation on why that happens.
        GradientPaint gp = new GradientPaint(x, y, c1, x, h - 60, c2);
        g2d.setPaint(gp);
        g2d.fill(s);
    }
  }
}

好吧,我能够回答我自己的问题!我猜的一切都是第一次。

我跟踪 JLabel 上的代码回到 class,它实际上在屏幕上被绘制到 BasicLabelUI Class。我研究并确定,如果我可以构建一个新的 class 来扩展这个 class,我可以简单地覆盖这个 class 的绘画部分。在测试并确定了从文本顶部到底部完全绘制两种颜色的最佳渐变之后,这就是我想出的:

public class SBLabelUI extends BasicLabelUI {

    @Override
    protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) { 
        if (l instanceof GradientLabel) {
            GradientLabel gl = (GradientLabel) l;
            Graphics2D g2d = (Graphics2D) g;
            Font f = gl.getFont();
            int h = gl.getFontMetrics(f).getHeight();

            GradientPaint gp = new GradientPaint(textX, textY, gl.c2, textX, Math.abs(textY-h), gl.c1);
            g2d.setPaint(gp);
            g2d.drawString(s, textX, textY);
        } else {
            super.paintEnabledText(l, g, s, textX, textY);
        }
    }
}

在我的渐变标签构造函数中,我简单地设置了 UI:

public GradientLabel(String text) {
    setText(text);
    this.setOpaque(false);
    setUI(new SBLabelUI());
}

它的工作很迷人: