如何防止JInternalFrame重新绘制重叠JInternalFrame
我有2个JInternalFrame每个都包含一个JPanel。一个JPanel(源代码)更新其GUI和一些响应鼠标事件的数据。另一个JPanel(目标)接收一个事件,指示数据已更改并相应地更新其外观。如何防止JInternalFrame重新绘制重叠JInternalFrame
如果源面板与目标面板重叠,则目标面板中的重新绘制会在源面板中触发重新绘制。同样,即使源面板遮挡了目标面板所需的更改,目标面板仍会重新绘制,并且仍会在源面板中触发重新绘制。
在我的现实生活应用程序中,这会产生性能问题,因为多个面板在鼠标拖动时会触发重新绘制,并且源面板会显示复杂的图像。
如何防止目标面板更新触发源面板中的重绘?
事情我已经尝试:
- 给参数重新粉刷这意味着只有目标面板的非重叠位重新刷新的问题,但对我来说,我不知道是哪个位的目标面板是可见的(我试过getVisibleRect和getClipBounds,但他们只是返回整个面板的大小)。在我的真实应用程序中,整个目标面板都会更新,而不仅仅是其中的一部分,所以我也不能以这种方式限制重新绘制。
-
基于现有的InternalFrameDemo创建了一个最小示例:同样的问题,请参阅下面的代码。该示例具有2个面板,如前所述,单击源面板(标记为Document 1)在该位置绘制红色框,并更新DataModel对象,该对象触发目标面板拾取的事件(标记为Document 2) ,它在本身的相同位置绘制一个红色框。在源paintComponent方法中设置断点,可以看到有2个更新,一个来自SourcePanel重绘,另一个来自TargetPanel重绘。
public class InternalFrameDemo extends JFrame implements ActionListener { JDesktopPane desktop; DataModel model = new DataModel(); public InternalFrameDemo() { super("InternalFrameDemo"); //Make the big window be indented 50 pixels from each edge //of the screen. int inset = 50; Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); setBounds(inset, inset, screenSize.width - inset*2, screenSize.height - inset*2); //Set up the GUI. desktop = new JDesktopPane(); //a specialized layered pane MyInternalFrame frame1 = createFrame(); //create first "window" MyInternalFrame frame2 = createFrame(); setContentPane(desktop); SourcePanel sp = new SourcePanel(model); frame1.add(sp); TargetPanel tp = new TargetPanel(model); frame2.add(tp); //Make dragging a little faster but perhaps uglier. desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE); } public void actionPerformed(ActionEvent e) { } //Create a new internal frame. protected MyInternalFrame createFrame() { MyInternalFrame frame = new MyInternalFrame(); frame.setVisible(true); //necessary as of 1.3 frame.setOpaque(true); desktop.add(frame); try { frame.setSelected(true); } catch (java.beans.PropertyVetoException e) {} return frame; } //Quit the application. protected void quit() { System.exit(0); } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Create and set up the window. InternalFrameDemo frame = new InternalFrameDemo(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Display the window. frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }
package components;
import javax.swing.JInternalFrame;
/* Used by InternalFrameDemo.java. */
public class MyInternalFrame extends JInternalFrame {
static int openFrameCount = 0;
static final int xOffset = 30, yOffset = 30;
public MyInternalFrame() {
super("Document #" + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
setSize(300,300);
setLocation(xOffset*openFrameCount, yOffset*openFrameCount);
}
}
package components;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SourcePanel extends JPanel {
int boxX, boxY;
int boxWidth = 10;
int boxHeight = 10;
public SourcePanel(DataModel data) {
addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent evt)
{
if (!SwingUtilities.isRightMouseButton(evt))
{
boxX = evt.getX();
boxY = evt.getY();
data.update(boxX,boxY);
repaint();
}
}
});
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.RED);
int x = Math.min(boxX, this.getWidth()-boxWidth);
int y = Math.min(boxY, this.getHeight()-boxHeight);
g.drawRect(x, y, boxWidth, boxHeight);
g.drawRect(x + 1, y + 1, boxWidth - 2, boxHeight - 2);
}
}
package components;
import java.awt.Color;
import java.awt.Graphics;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
public class TargetPanel extends JPanel implements PropertyChangeListener {
int boxX, boxY;
int boxWidth = 10;
int boxHeight = 10;
public TargetPanel(DataModel data) {
data.addPropertyChangeListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent evt)
{
//draw something in response to the data change
boxX = ((int[])evt.getNewValue())[0];
boxY = ((int[])evt.getNewValue())[1];
repaint();
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
int x = Math.min(boxX, this.getWidth()-boxWidth);
int y = Math.min(boxY, this.getHeight()-boxHeight);
g.fillRect(x, y, boxWidth, boxHeight);
}
}
package components;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class DataModel {
int datax = 10;
int datay = 10;
public DataModel()
{
}
public void update(int x, int y)
{
int[] olddata = new int[]{datax,datay};
datax = x;
datay = y;
int[] newdata = new int[]{datax,datay};
changeSupport.firePropertyChange("DataChange", olddata, newdata);
}
protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
this);
public void addPropertyChangeListener(PropertyChangeListener listener)
{
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener)
{
changeSupport.removePropertyChangeListener(listener);
}
}
(版权声明原始JInternalFrame的示例代码:)
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
您可以缓存鼠标变化,做他们的时间间隔,或在鼠标跟踪结束。
最容易的可能是尝试推迟重画。
@Override
public void propertyChange(PropertyChangeEvent evt)
{
//draw something in response to the data change
boxX = ((int[])evt.getNewValue())[0];
boxY = ((int[])evt.getNewValue())[1];
repaint(200L);
}
实际上重绘几次后,呼吁repaint(200L);
。 (对于我的感觉,我选择了第五秒的高值)。
感谢您提出了一个有趣的建议,我不知道这是重新绘制的选项。我在我的真实代码中尝试了各种值,但它并不能真正帮助性能。 – smiley
@smiley我很害怕这个。其他一切都是工作。我希望(实际上确信)你在变化事件中检查了周期:在目标面板上的鼠标监听器,一个变化导致几个变化,所有这些变化都是目标面板上的重复变化或任何其他变化。复杂的涂料代码。 –
每个'paintComponent'方法都必须在执行任何其他操作之前调用'super.paintComponent(g);'。请参阅https://docs.oracle.com/javase/tutorial/uiswing/painting/。 – VGR
@VGR好的,添加了 - 但它对我的问题没有任何影响 – smiley