问题描述
我正在开发一个工作程序(注意:我不能共享完整的代码,因为它是大部分受保护的工作产品,但我会分享我能做的一切)。在应用程序中,我有JPanels应用了背景图像。这些面板中的一些还附有鼠标听众。我的管理层希望有一个视觉线索,可以点击面板来启动操作。为此,我在JPanel的顶部用背景图像覆盖了一个透明的JPanel,并附加了一个MouseListener,键入了mouseEntered / Exited事件。当鼠标进入图像面板时,重叠的面板将从透明切换为半透明,并在鼠标退出时返回。
在Linux下,这非常有效。在Windows下......我很秃,所以我不能把头发撕掉。似乎正在发生的事情是,当我移动鼠标时会出现某种图像缓存,并且mouseEnter事件导致鼠标在一秒钟之前被绘制到帧中;即,如果我将鼠标放在GUI上的附近按钮上。然后将鼠标悬停在面板上,我会看到按钮出现在面板中,周围有GUI。
图像面板包含在不透明的JInternalFrame中。 / p>
另外需要注意的是,如果我在应用程序中执行会导致图像更改的内容(例如,从JComboBox中进行新选择),则图像会按预期重新绘制。无论遇到什么麻烦,它似乎与突出显示以及如何重新绘制/重绘图像有关。
我应该为Windows做什么?
提前致谢。
。当面板打开时,Start.png就是两个操作系统中的样子。 GoodMouseEnter.png是Linux下的mouseEnter事件。 BadMouseEnter.png& badMouseExit.png是它在Windows下的样子。
这是我用来创建图像面板的类(编辑:这个例子是自包含的):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
公共类ImageTest {
JFrame frm = null;
ImagePanel imgP = null;
public static void main(String [] args)抛出MalformedURLException
{
ImageTest it = new ImageTest();
it.initialize();
}
public void initialize()抛出MalformedURLException
{
String path =file:/ C:/CylinderTank.png;
URL imagePath =新网址(路径);
System.out.println(imagePath.toString());
frm = new JFrame();
imgP = new ImagePanel(true);
int fW = 500;
int fH = 700;
int pW = 450;
int pH = 650;
frm.setLayout(null);
frm.setPreferredSize(new Dimension(fW,fH));
frm.setSize(fW,fH);
imgP.getFilterPanel()。addMouseListener(new PanelListener());
imgP.useCustomSizing(pW,pH);
imgP.setImageURL(imagePath);
imgP.setBounds(0,0,pW,pH);
frm.add(imgP);
frm.pack();
frm.setVisible(true);
}
私有类PanelListener实现MouseListener {
public PanelListener(){}
@Override
public void mouseClicked(MouseEvent e) {}
@覆盖
公共无效的mousePressed(的MouseEvent E){}
@覆盖
公共无效的mouseReleased(的MouseEvent E){}
@覆盖
public void mouseEntered(MouseEvent e)
{
imgP.highlightImage(true);
imgP.repaint();
}
@Override
public void mouseExited(MouseEvent e)
{
imgP.highlightImage(false);
imgP.repaint();
}
}
}
@SuppressWarnings(serial)
class ImagePanel扩展JPanel实现ImageObserver {
private final JPanel filterPanel;
private BufferedImage图片;
private Dimension panelSize;
私人最终工具包套件;
private boolean highlight = false;
private URL imagePath;
private int imgW,imgH;
private final Color blueFilter = new Color(0,0,255,38);
private final Color redFilter = new Color(255,0,0,38);
private final Color greenFilter = new Color(0,255,0,38);
private final Color clear = new Color(0,0,0,0);
private final Color bgColor = new Color(116,169,255,255);
private boolean customSize = false;
public ImagePanel(boolean opaque)
{
super();
this.kit = Toolkit.getDefaultToolkit();
setLayout(null);
setOpaque(opaque);
setBackground(bgColor);
filterPanel = new JPanel();
filterPanel.setBackground(clear);
}
public ImagePanel(URL imagePath,boolean opaque)
{
super();
this.imagePath = imagePath;
this.kit = Toolkit.getDefaultToolkit();
setLayout(null);
setOpaque(opaque);
setBackground(bgColor);
filterPanel = new JPanel();
filterPanel.setBackground(clear);
readImage();
}
@Override
protected void paintComponent(Graphics g)
{
Graphics2D g2D =(Graphics2D)g;
if(highlight)
filterPanel.setBackground(blueFilter);
else
filterPanel.setBackground(clear);
int X = 0,Y = 0;
if(image!= null)
{
image.flush();
kit.prepareImage(image,-1,-1,this);
if(customSize)
{
X =(panelSize.width - imgW)/ 2;
Y =(panelSize.height - imgH)/ 2;
}
if(isOpaque())
g2D.drawImage(image,X,Y,bgColor,this);
else
g2D.drawImage(image,X,Y,this);
}
else
super.paintComponent(g2D);
}
public void highlightImage(boolean highlight)
{
this.highlight = highlight;
}
private void readImage()
{
try
{
image = ImageIO.read(imagePath);
imgW = image.getWidth();
imgH = image.getHeight();
if(customSize)
panelSize = getPreferredSize();
else
panelSize = new Dimension(imgW,imgH);
setPreferredSize(panelSize);
setMinimumSize(panelSize);
setMaximumSize(panelSize);
int X =(panelSize.width - imgW)/ 2;
int Y =(panelSize.height - imgH)/ 2;
filterPanel.setBounds(X,Y,imgW,imgH);
add(filterPanel);
}
catch(IOException ex)
{
Logger.getLogger(ImagePanel.class.getName())。log(Level.SEVERE,null,ex);
}
}
public void setImageURL(URL img)
{
this.imagePath = img;
readImage();
}
public Dimension getDisplayedImageSize()
{
if(image == null)
return null;
返回新维度(imgW,imgH);
}
public JPanel getFilterPanel()
{
return filterPanel;
}
public void useCustomSizing(int W,int H)
{
if(W< 0)
W = getPreferredSize()。width ;
if(H< 0)
H = getPreferredSize()。height;
if((W> 0)||(H> 0))
customSize = true;
尺寸cDim =新尺寸(W,H);
setPreferredSize(cDim);
setMinimumSize(cDim);
setMaximumSize(cDim);
repaint();
}
} //结束类ImagePanel
有一系列问题出现......
- 过度使用
null
布局,我会回到那个...... - 使用叠加面板
- 修改在paint方法的上下文中的组件
- 依赖于魔术数字而不是已知值
- 打破画颜链...
所有这些都是对你的阴谋......
第一件事你需要知道的是Swing只知道如何绘制不透明或透明的组件,并且只有当这些组件被标记为不透明或不透明时。如果你使用带有alpha值的颜色,Swing也不知道需要在组件下面绘画。
你需要知道的第二件事是 Graphics
context是共享资源。也就是说,在绘制周期中更新了每个组件,获得了SAME Graphics
上下文。
主要问题是,你的 filterPanel
使用 alpha
颜色,但是Swing不知道它应该在它下面绘画,所以它只是用你选择的颜色填充可用区域,但因为它是alpha颜色,它不能完全清除 Graphics
上下文,所以你最终会得到油漆文物被遗忘...
事实是,你不需要 filterPane
或者更多重要的是,不需要像你一样使用它。你不应该从paint方法中更新任何组件的状态,这将导致组件每次都请求重新绘制,这将快速消耗你的CPU周期,直到你的系统不能运行...
您可以使用类似的东西来实现类似的结果...
@Override
protected void paintComponent(Graphics g){
Graphics2D g2D =(Graphics2D)g;
super.paintComponent(g2D);
int X = 0,Y = 0;
if(image!= null){
if(customSize){
X =(panelSize.width - imgW)/ 2;
Y =(panelSize.height - imgH)/ 2;
}
g2D.drawImage(image,X,Y,this);
}
if(highlight){
g2D.setColor(blueFilter);
g2D.fillRect(X,Y,image.getWidth(),image.getHeight());
}
}
看看,和了解更多详情。
依赖预先计算的值也可能导致问题。组件的大小应该通过使用它的 getWidth
和 getHeight
属性来确定,因为值可能在绘制周期之间发生了变化...
我还鼓励您使用 ImageIO
而不是工具包
,它会...
- 返回完整的已实现图像,而不是将加载加载到后台线程
- 如果由于某种原因无法读取文件,则提出异常,而不是默默地... ...
有关详情,请参阅
这是我如何解决问题的一个基本例子......
import java.awt.Color中;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
公共类MouseOverTest {
public static void main(String [] args){
new MouseOverTest();
}
公共MouseOverTest(){
EventQueue.invokeLater(新的Runnable(){
@覆盖
公共无效的run(){
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex){
ex.printStackTrace();
}
尝试{
BufferedImage的背景= ImageIO.read(新文件( C:\\hold\\thumbnails\\_MTCGAC__Pulling_Cords_by_Dispozition.png));
JFrame frame = new JFrame(Testing);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(background));
frame .pack();
frame.setLocationR elativeTo(null);
frame.setVisible(true);
} catch(IOException exp){
exp.printStackTrace();
}
}
});
}
公共静态类TestPane扩展JPanel {
protected static final Color BLUE_FILTER = new Color(0,0,255,38);
私有BufferedImage背景;
private Rectangle imageBounds;
private boolean mouseInTheHouse;
public TestPane(BufferedImage background){
this.background = background;
MouseAdapter毫安=新MouseAdapter(){
@覆盖
公共无效的mouseMoved(的MouseEvent E){
mouseInTheHouse = getImageBounds()包含(e.getPoint())。
repaint();
}
@Override
public void mouseExited(MouseEvent e){
mouseInTheHouse = false;
repaint();
}
};
addMouseMotionListener(ma);
addMouseListener(ma);
}
@Override
public Dimension getPreferredSize(){
return background == null? new Dimension(200,200):new Dimension(background.getWidth(),background.getHeight());
}
@Override
public void invalidate(){
imageBounds = null;
super.invalidate();
}
protected Rectangle getImageBounds(){
if(imageBounds == null){
if(background!= null) {
int x =(getWidth() - background.getWidth())/ 2;
int y =(getHeight() - background.getHeight())/ 2;
imageBounds = new Rectangle(x,y,background.getWidth(),background.getHeight());
} else {
imageBounds = new Rectangle(0,0,0,0);
}
}
return imageBounds;
}
@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d =(Graphics2D)g.create();
Rectangle bounds = getImageBounds();
if(background!= null){
g2d.drawImage(background,bounds.x,bounds.y,this);
}
if(mouseInTheHouse){
g2d.setColor(BLUE_FILTER);
g2d.fill(bounds);
}
g2d.dispose();
}
}
}
I'm developing a program for work (notice: I can not share complete code, as it is in large part protected work product, but I will share everything I can). In the application, I have JPanels that have background images applied to them. Some of these panels also have mouse listeners attached & my management wants there to be a visual clue that the panel can be clicked on to initiate an action. To that end, I've overlaid a transparent JPanel on top of the JPanel with the background image, and attached a MouseListener to that, keying off the mouseEntered/Exited events. When the mouse enters the image panel, the overlaid panel will switch from transparent to translucent, and back when the mouse exits.
Under Linux, this works perfectly. Under Windows... it's a good thing I'm bald so I can't tear my hair out. What appears to be happening is that some kind of image caching is occuring as I move the mouse around, and the mouseEnter event is causing whatever the mouse was over a second ago to be painted into the frame; i.e. if I place the mouse over a nearby button on the GUI & then mouse over the panel, I'll see the button appear in the panel, with the surrounding GUI.
The Image Panel is contained within a JInternalFrame that is opaque.
One other thing to note, if I do something in the application that would cause the image to change (e.g. making a new selection from a JComboBox), the image repaints as expected. Whatever the trouble is, it seems related to the highlighting and how the image is being repainted/redrawn.
What am I not doing for Windows that I should be?
Thanks in advance.
Here is a link to some images. Start.png is what it looks like in both OS's when the panel opens. GoodMouseEnter.png is what the mouseEnter event looks like under Linux. BadMouseEnter.png & badMouseExit.png are what it looks like under Windows.
This is the Class I am using to create the image panel (EDIT: This example is self contained):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageTest {
JFrame frm = null;
ImagePanel imgP = null;
public static void main(String[] args) throws MalformedURLException
{
ImageTest it = new ImageTest();
it.initialize();
}
public void initialize() throws MalformedURLException
{
String path = "file:/C:/CylinderTank.png";
URL imagePath = new URL(path);
System.out.println(imagePath.toString());
frm = new JFrame();
imgP = new ImagePanel(true);
int fW = 500;
int fH = 700;
int pW = 450;
int pH = 650;
frm.setLayout(null);
frm.setPreferredSize(new Dimension(fW,fH));
frm.setSize(fW,fH);
imgP.getFilterPanel().addMouseListener(new PanelListener());
imgP.useCustomSizing(pW, pH);
imgP.setImageURL(imagePath);
imgP.setBounds(0, 0, pW, pH);
frm.add(imgP);
frm.pack();
frm.setVisible(true);
}
private class PanelListener implements MouseListener {
public PanelListener() { }
@Override
public void mouseClicked(MouseEvent e) { }
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e)
{
imgP.highlightImage(true);
imgP.repaint();
}
@Override
public void mouseExited(MouseEvent e)
{
imgP.highlightImage(false);
imgP.repaint();
}
}
}
@SuppressWarnings("serial")
class ImagePanel extends JPanel implements ImageObserver {
private final JPanel filterPanel;
private BufferedImage image;
private Dimension panelSize;
private final Toolkit kit;
private boolean highlight = false;
private URL imagePath;
private int imgW, imgH;
private final Color blueFilter = new Color(0, 0, 255, 38);
private final Color redFilter = new Color(255, 0, 0, 38);
private final Color greenFilter = new Color(0, 255, 0, 38);
private final Color clear = new Color(0, 0, 0, 0);
private final Color bgColor = new Color(116, 169, 255, 255);
private boolean customSize = false;
public ImagePanel(boolean opaque)
{
super();
this.kit = Toolkit.getDefaultToolkit();
setLayout(null);
setOpaque(opaque);
setBackground(bgColor);
filterPanel = new JPanel();
filterPanel.setBackground(clear);
}
public ImagePanel(URL imagePath, boolean opaque)
{
super();
this.imagePath = imagePath;
this.kit = Toolkit.getDefaultToolkit();
setLayout(null);
setOpaque(opaque);
setBackground(bgColor);
filterPanel = new JPanel();
filterPanel.setBackground(clear);
readImage();
}
@Override
protected void paintComponent(Graphics g)
{
Graphics2D g2D = (Graphics2D) g;
if (highlight)
filterPanel.setBackground(blueFilter);
else
filterPanel.setBackground(clear);
int X = 0, Y = 0;
if (image != null)
{
image.flush();
kit.prepareImage(image, -1, -1, this);
if (customSize)
{
X = (panelSize.width - imgW) / 2;
Y = (panelSize.height - imgH) / 2;
}
if (isOpaque())
g2D.drawImage(image, X, Y, bgColor, this);
else
g2D.drawImage(image, X, Y, this);
}
else
super.paintComponent(g2D);
}
public void highlightImage(boolean highlight)
{
this.highlight = highlight;
}
private void readImage()
{
try
{
image = ImageIO.read(imagePath);
imgW = image.getWidth();
imgH = image.getHeight();
if (customSize)
panelSize = getPreferredSize();
else
panelSize = new Dimension(imgW, imgH);
setPreferredSize(panelSize);
setMinimumSize(panelSize);
setMaximumSize(panelSize);
int X = (panelSize.width - imgW) / 2;
int Y = (panelSize.height - imgH) / 2;
filterPanel.setBounds(X, Y, imgW, imgH);
add(filterPanel);
}
catch (IOException ex)
{
Logger.getLogger(ImagePanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void setImageURL(URL img)
{
this.imagePath = img;
readImage();
}
public Dimension getDisplayedImageSize()
{
if (image == null)
return null;
return new Dimension(imgW, imgH);
}
public JPanel getFilterPanel()
{
return filterPanel;
}
public void useCustomSizing(int W, int H)
{
if (W < 0)
W = getPreferredSize().width;
if (H < 0)
H = getPreferredSize().height;
if ((W>0) || (H>0))
customSize = true;
Dimension cDim = new Dimension(W,H);
setPreferredSize(cDim);
setMinimumSize(cDim);
setMaximumSize(cDim);
repaint();
}
}//end class ImagePanel
There are a cascade of problems presenting themselves...
- The over use of
null
layouts, I'll get back to that... - The use of a "overlay" panel
- Modification of a component from within the context of the a paint method
- Reliance of "magic" numbers instead of known values
- Breaking the paint chain...
All these things are conspiring against you...
The first thing you need to know is Swing only knows how to paint opaque or transparent components and only when those components are flagged as been opaque or not. If you use a color with an alpha value, Swing doesn't know that needs to paint under the component as well.
The second thing you need to know is that the Graphics
context is a shared resource. That is, every component been updated during a paint cycle get the SAME Graphics
context.
The MAIN problem is, your filterPanel
is using a alpha
color, but Swing doesn't know that it should be painting under it, so it simply "fills" the available area with the color you have chosen, but because it's a alpha color, it doesn't completely clean the Graphics
context, so you will end up with paint artifacts been left behind...
The fact is, you don't need the filterPane
or, more importantly, don't need to use it the way you are. You should NEVER update the state of any component from within a paint method, this will cause the component to request a repaint, every time, which will quickly consume your CPU cycles till you system won't run...
You could achieve a simular resulting using something like...
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
super.paintComponent(g2D);
int X = 0, Y = 0;
if (image != null) {
if (customSize) {
X = (panelSize.width - imgW) / 2;
Y = (panelSize.height - imgH) / 2;
}
g2D.drawImage(image, X, Y, this);
}
if (highlight) {
g2D.setColor(blueFilter);
g2D.fillRect(X, Y, image.getWidth(), image.getHeight());
}
}
Take a look at Painting in AWT and Swing, Performing Custom Painting and 2D Graphics for more details.
The reliance on pre-calculated values could also cause you issues. The size of a component should be determined by using it's getWidth
and getHeight
properties as the values could have changed between paint cycles...
I would also encourage you to use ImageIO
over Toolkit
, it will...
- Return a full realised image instead of off loading the loading to a background thread
- Raise an exception if it can't read the file for some reason, rather the silently failing...
See Reading/Loading an Image for more details
This is a basic example of how I might approach the problem...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MouseOverTest {
public static void main(String[] args) {
new MouseOverTest();
}
public MouseOverTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
BufferedImage background = ImageIO.read(new File("C:\\hold\\thumbnails\\_MTCGAC__Pulling_Cords_by_Dispozition.png"));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane(background));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public static class TestPane extends JPanel {
protected static final Color BLUE_FILTER = new Color(0, 0, 255, 38);
private BufferedImage background;
private Rectangle imageBounds;
private boolean mouseInTheHouse;
public TestPane(BufferedImage background) {
this.background = background;
MouseAdapter ma = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
mouseInTheHouse = getImageBounds().contains(e.getPoint());
repaint();
}
@Override
public void mouseExited(MouseEvent e) {
mouseInTheHouse = false;
repaint();
}
};
addMouseMotionListener(ma);
addMouseListener(ma);
}
@Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
@Override
public void invalidate() {
imageBounds = null;
super.invalidate();
}
protected Rectangle getImageBounds() {
if (imageBounds == null) {
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
imageBounds = new Rectangle(x, y, background.getWidth(), background.getHeight());
} else {
imageBounds = new Rectangle(0, 0, 0, 0);
}
}
return imageBounds;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = getImageBounds();
if (background != null) {
g2d.drawImage(background, bounds.x, bounds.y, this);
}
if (mouseInTheHouse) {
g2d.setColor(BLUE_FILTER);
g2d.fill(bounds);
}
g2d.dispose();
}
}
}
这篇关于当我将背景图像绘制到JPanel上时,它在Windows下的行为与在Linux下的行为不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!