在半透明JPanel上绘制不同的非矩形多边形会导致边缘难看

时间:2013-11-27 20:20:23

标签: java swing transparency paintcomponent graphics2d

我正在为游戏制作自定义菜单,并尝试绘制组件时遇到问题。 这个想法是,让图片或视频在后台运行,其上面有一个面板,其中包含一些自定义菜单项。

包含菜单项的面板应该是半透明的,这就是我猜的问题。

因为有点难以解释我已经建立了一个展示这个问题的例子。在鼠标悬停时重新绘制菜单项没有任何问题,但看起来重绘也会影响不应绘制的面板部分,因为实际菜单项的形状不适合面板矩形形状。这导致我的菜单项具有难看的黑色边缘,不应该在那里(右上角和左下角)。

希望你们知道这个原因的解决方案我开始变得非常恼火。我尝试了我在这里或网上其他地方找到的所有东西,但看起来所有其他问题只需要处理重绘一个不会改变的形状。

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class TransparentDrawTest extends JPanel implements MouseListener {

 private final static Dimension SIZE = new Dimension(400, 50);

 private boolean isSelected;
 private boolean isClicked;

 public static void main(String[] args) {

    JFrame testFrame = new JFrame();
    testFrame.setSize(500, 100);
    testFrame.setResizable(false);
    testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    testFrame.setLocationRelativeTo(null);
    testFrame.setBackground(Color.WHITE);

    JPanel semiTransPanel = new JPanel();
    semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));
    semiTransPanel.setOpaque(true);
    semiTransPanel.add(new TransparentDrawTest());

    testFrame.add(semiTransPanel);
    testFrame.setVisible(true);
}

public TransparentDrawTest() {

    this.addMouseListener(this);

    this.setSize(SIZE);
    this.setMinimumSize(SIZE);
    this.setMaximumSize(SIZE);
    this.setPreferredSize(SIZE);

    this.setOpaque(false);
}

@Override
public void paintComponent(Graphics g) {

    Graphics2D g2d = (Graphics2D) g;

    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
    g2d.fillRect(0, 0, 400, 50);

    int[] pointsX;
    int[] pointsY;

    if (this.isSelected) {
        pointsX = new int[] { 2, 348, 398, 48 };
        pointsY = new int[] { 2, 2, 48, 48 };
    }
    else {
        pointsX = new int[] { 2, 298, 348, 48 };
        pointsY = new int[] { 2, 2, 48, 48 };
    }

    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setStroke(new BasicStroke(5));
    g2d.setColor(Color.WHITE);
    g2d.drawPolygon(pointsX, pointsY, 4);

    if (this.isClicked)
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
    else
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));

    g.fillPolygon(pointsX, pointsY, 4);
}

@Override
public void mouseClicked(MouseEvent e) {
}

@Override
public void mouseEntered(MouseEvent e) {
    this.isSelected = true;
    this.repaint();
}

@Override
public void mouseExited(MouseEvent e) {
    this.isSelected = false;
    this.repaint();
}

@Override
public void mousePressed(MouseEvent e) {
    this.isClicked = true;
    this.repaint();
}

@Override
public void mouseReleased(MouseEvent e) {
    this.isClicked = false;
    this.repaint();
}
}

2 个答案:

答案 0 :(得分:2)

至少有两件事我能看到会给你带来麻烦...

首先,您正在更改Graphics上下文的字母组合,但您没有休息....

Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));

Graphics上下文是一个共享资源,在当前绘制周期中绘制的所有组件都将被赋予相同的Graphics实例,这意味着您对其所做的任何更改都将传递给正在绘制的其他组件。

相反,您应该创建Graphics上下文的临时副本,然后使用它...

Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
//...
// If you create it, you must dispose of it...
g2d.dispose();

其次,您将不透明组件的背景颜色设置为半透明值...

semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));

问题是,Swing只处理组件而且不透明或透明。通过这样做,Swing不会知道它也应该更新这个下面的组件。基本上它会认为组件是不透明的(因为它是)并且不会更新下面的组件......

基本上,你需要做一些类似于你使用TransparentDrawTest面板所做的事情,并假装它,例如......

JPanel semiTransPanel = new JPanel() {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); 
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
        g2d.setColor(getBackground());
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g2d.dispose();
    }
};
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f));
semiTransPanel.setOpaque(false);

我也无法弄清楚你为什么要这样做......

g2d.fillRect(0, 0, 400, 50);

TransparentDrawTest窗格中...

你应该首先调用super.paintComponent(g),这实际上是这个方法的作用,它清除了绘制的Graphics上下文...等等;)

更新了示例代码...

这是我用来测试代码的示例代码......

Slide

Ps:我改变了半透明面板的背景颜色,所以我可以看到差异......

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class TransparentDrawTest extends JPanel implements MouseListener {

    private final static Dimension SIZE = new Dimension(400, 50);

    private boolean isSelected;
    private boolean isClicked;

    public static void main(String[] args) {

        JFrame testFrame = new JFrame();
        testFrame.setSize(500, 100);
        testFrame.setResizable(false);
        testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        testFrame.setLocationRelativeTo(null);
        testFrame.setBackground(Color.WHITE);

        JPanel semiTransPanel = new JPanel() {

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g); 
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setColor(getBackground());
                g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
                g2d.fillRect(0, 0, getWidth(), getHeight());
                g2d.dispose();
            }

        };
        semiTransPanel.setOpaque(false);
        semiTransPanel.setBackground(Color.RED);
        semiTransPanel.add(new TransparentDrawTest());

        testFrame.add(semiTransPanel);
        testFrame.setVisible(true);
    }

    public TransparentDrawTest() {

        this.addMouseListener(this);

        this.setSize(SIZE);
        this.setMinimumSize(SIZE);
        this.setMaximumSize(SIZE);
        this.setPreferredSize(SIZE);

        this.setOpaque(false);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();

        int[] pointsX;
        int[] pointsY;

        if (this.isSelected) {
            pointsX = new int[]{2, 348, 398, 48};
            pointsY = new int[]{2, 2, 48, 48};
        } else {
            pointsX = new int[]{2, 298, 348, 48};
            pointsY = new int[]{2, 2, 48, 48};
        }

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setStroke(new BasicStroke(5));
        g2d.setColor(Color.WHITE);
        Composite comp = g2d.getComposite();

        if (this.isClicked) {
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
        } else {
            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
        }

        g2d.fillPolygon(pointsX, pointsY, 4);
        g2d.setComposite(comp);
        g2d.drawPolygon(pointsX, pointsY, 4);
        g2d.dispose();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        this.isSelected = true;
        this.repaint();
    }

    @Override
    public void mouseExited(MouseEvent e) {
        this.isSelected = false;
        this.repaint();
    }

    @Override
    public void mousePressed(MouseEvent e) {
        this.isClicked = true;
        this.repaint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        this.isClicked = false;
        this.repaint();
    }
}

答案 1 :(得分:0)

有关可能的问题和简单的解决方案,请参阅Backgrounds With Transparency

//testFrame.add(semiTransPanel);
testFrame.add( new AlphaContainer(semiTransPanel) );