简体   繁体   中英

How to prevent a JInternalFrame from repainting an overlapping JInternalFrame

I have 2 JInternalFrames each containing a JPanel. One JPanel (the source) updates its GUI and some data in response to a mouse event. The other JPanel (the target) receives an event indicating the data has changed and updates its appearance accordingly.

If the source panel overlaps the target panel, the repaint in the target panel triggers a repaint in the source panel. Also even if the required change to the target panel is obscured by the source panel, the target panel still repaints, and still triggers a repaint in the source panel.

In my real life application, this creates a performance issue, as multiple panels are triggering repaints on mouse drag, and the source panel has a complex image to display.

How can I prevent the target panel update from triggering a repaint in the source panel?

Things I've tried:

  • giving parameters to repaint which mean only a non-overlapped bit of the target panel is redrawn stops the problem, BUT in my case I don't know which bit of the target panel is visible (I tried getVisibleRect and getClipBounds but they just return the whole panel size). In my real application the whole target panel is updated, not just part of it, so I also can't restrict the repaint that way.
  • created a minimal example based on an existing InternalFrameDemo: same problem, see code below. The example has 2 panels as I've described, clicking in the source panel (labelled Document 1) draws a red box at that location, and updates the DataModel object, which fires an event picked up by the target panel (labelled Document 2), which draws a red box at the same location in itself. Setting a breakpoint in the source paintComponent method, you can see that there are 2 updates, one from the SourcePanel repaint and one from the TargetPanel repaint.

     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);
    }

}

(Copyright notice for the original JInternalFrame example code:)

/*
 * 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.
 */ 

You could cache the mouse changes and do them in intervals, or at the mouse tracking end.

The most easy probably is to try a postponed repaint.

@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);
}

Actually repainting after several callss to repaint(200L); . (For my feeling I have picked a high value of a fifth second.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM