1

I'm trying to do a visual drag and drop in my Swing application using a GlassPane, but am running into the problem that the drag image lags behind the mouse pointer, sometimes considerably. How do I solve this? My GlassPane code is below. Thanks!

package dragui;

import java.awt.*;
import javax.swing.*;
import java.awt.event.MouseEvent;
import javax.swing.event.MouseInputAdapter;

public class GlassPane extends JComponent {
    private int x=0, y=0, k=25,z=1;
    private boolean showDot;
    private MouseInputAdapter mia = new MouseInputAdapter(){

        @Override
        public void mouseDragged(MouseEvent me) {
            setPos(me.getX(), me.getY());
        }

        @Override
        public void mousePressed(MouseEvent me) {
            setShow(true);
            setPos(me.getX(), me.getY());   
        }

        @Override
        public void mouseReleased(MouseEvent me){
            setShow(false);
            setVisible(false);
        }
    };

    public void setShow(boolean b){ this.showDot = b; }

    public void paint(Graphics g) {
        if (showDot) {
            g.setColor(new Color(0.0f, 0.25f, 1.0f));
            g.fillOval(x - k, y - k, 2*k, 2*k);
        }
    }

    public void setPos(int x, int y) {
        int tmpX = this.x, tmpY = this.y; 
        this.x = x; this.y = y;
        repaint(tmpX - k , tmpY-k, 2*k+5, 2*k+5);
        repaint(this.x-k, this.y-k, 2*k+5, 2*k+5);        
    }

    public GlassPane() {
        addMouseListener(mia); 
        addMouseMotionListener(mia);
        setOpaque(false);
    }
}
Frobba Bobba
  • 45
  • 1
  • 7
  • Are you sure you want to override `paint(...)`? Shouldn't you be overriding `paintComponent(...)`? `paint(...)` will paint the component's contents, borders, and children. – mre Jun 23 '11 at 12:21
  • What do you mean with _sometimes considerably_. I dont get the cursor out of the circle – oliholz Jun 23 '11 at 12:33
  • @mre I don't think it makes a difference performance wise, as my paint isn't doing all of those things and doesn't call super.paint – Frobba Bobba Jun 23 '11 at 13:37
  • @oliholz if I just hold down the mouse button and move the mouse around, I'll see things like the dot freezing for a second every so often, or the cursor being outside (in "front" of) the dot for a while. Also, I've got some controls on the contentPane below the GlassPane: a SplitPane containing a scrolling JList with ~20 items (rendered as JPanels containing 3 JLabels by a custom ListCellRenderer) and a JLayeredPane with some JLabels displaying images -- so that'd be a difference from your example, since there's redrawing involved – Frobba Bobba Jun 23 '11 at 13:40

2 Answers2

2

According to the Javadoc,

A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.

And this is because paint(...) actually delegates the work of painting to three protected methods: paintComponent, paintBorder, and paintChildren. That being said, try replacing paint(...) with this

@Override
protected void paintComponent(Graphics g) {
    if (showDot) {
        Graphics gCopy = g.create();

        gCopy.setColor(new Color(0.0f, 0.25f, 1.0f));
        gCopy.fillOval(x - k, y - k, 2*k, 2*k);

        gCopy.dispose();
    }
}
mre
  • 43,520
  • 33
  • 120
  • 170
  • 2
    @mKorbel, Are you sure he's talking about an _actual_ [Glass Pane](http://download.oracle.com/javase/tutorial/uiswing/components/rootpane.html), or about his GlassPane custom component? – mre Jun 23 '11 at 12:46
  • @mre hmmmm that's the question, in other hand you said good reason why its doesn't works – mKorbel Jun 23 '11 at 12:52
  • 1
    @mre hmmm I checked example from this tutorial and looks like as you are right, GlassPane and RootPane could be extended with JComponents, then paintComponent() is correct, agreed good to know +1 – mKorbel Jun 23 '11 at 12:59
  • @mre Why are you instantiating gCopy? – Frobba Bobba Jun 23 '11 at 13:31
  • @mre Also, what do you mean by an "actual Glass Pane"? My understanding of the Root Pane examples was that you *must* designate your own component using setGlassPane? – Frobba Bobba Jun 23 '11 at 13:32
  • @Frobba Bobba, With regard to your first question, this technique avoids clobbering the state of the original graphics object. With regard to your second question, see @mKorbel's comment(s). – mre Jun 23 '11 at 13:35
  • @Frobba Bobba, Then your problem is elsewhere. – mre Jun 23 '11 at 13:43
  • @mre Any suggestions as to diagnosing where? I'm at my wits end – Frobba Bobba Jun 23 '11 at 13:43
  • @Frobba Bobba, your double invocation of `repaint(...)` in `setPos(...)` is interesting... – mre Jun 23 '11 at 13:45
  • @mre As far as I know, I have to do that, so that I erase the previous dot draw the new one, but Swing collapses those calls into a single operation – Frobba Bobba Jun 23 '11 at 13:47
  • @Frobba Bobba, are you sure Swing collapses those calls into a single operation? I know the `EDT` will do that with other (albeit same) events, but I'm not sure if this applies to invoking repaint, especially with two distinct calls to repaint – mre Jun 23 '11 at 13:51
  • 1
    @mre " the asynchronous repaint() will cause multiple overlapping requests to be collapsed efficiently" --http://java.sun.com/products/jfc/tsc/articles/painting/#paint_process – Frobba Bobba Jun 23 '11 at 13:59
  • @mre That same article mentions paintImmediately, which I'm going to try now – Frobba Bobba Jun 23 '11 at 14:01
  • @Frobba Bobba, thanks for the link to the article! I suppose it's just a little confusing to me because they're referencing repaint without any parameters...I wasn't sure if repaint with parameters was treated any differently, which I thought it would be since I can't imagine collapsing them would result in the desired paint job... – mre Jun 23 '11 at 14:03
  • @mre, I think it gives the desired paint since you'd call repaint with parameters describing various rectangles of the screen you want updated, and then that would get collapsed into a single "unioned" rectangle which would have its area repainted? – Frobba Bobba Jun 23 '11 at 14:08
  • @Frobba Bobba, you would think so, right? but that's a lot of assuming. why not just invoke repaint once and explicitly pass the entire affected area? -- couldn't hurt! :) – mre Jun 23 '11 at 14:09
  • 1
    @Frobba Bobba, report back please! I apologize for not having the answer readily available :/ – mre Jun 23 '11 at 14:14
  • testing it now, will report in a minute or so – Frobba Bobba Jun 23 '11 at 14:24
  • @Frobba Bobba, my pleasure! thanks for being so responsive -- it shows that you care, and that's what counts. :) – mre Jun 23 '11 at 14:25
  • 1
    @mre I will vote this as the correct answer though, as it's been a very useful discussion. Thanks again! – Frobba Bobba Jun 23 '11 at 14:30
  • @Frobba Bobba, that's too bad! I wish I could help you out some more. If you have the time, perhaps upload some more code (provide a link to source, if you want) and I can play around with it if you haven't figured out what's truly wrong. – mre Jun 23 '11 at 14:35
0

perhaps you should not creating a new Color(0.0f, 0.25f, 1.0f) on every repaint.

For me the performance is quiet nice.

public class Example {
    static public void main( String[] s ) {
        EventQueue.invokeLater( new Runnable() {
            public void run() {
                JFrame frame = new JFrame();
                frame.setBounds( 50, 50, 600, 600 );
                frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
                frame.setGlassPane( new JComponent() {
                    {
                        MouseInputAdapter mia = new MouseInputAdapter(){
                            public void mouseDragged(MouseEvent me) {
                                setPos(me.getX(), me.getY());
                            }
                            public void mousePressed(MouseEvent me) {
                                setShow(true);
                                setPos(me.getX(), me.getY());   
                            }
                            public void mouseReleased(MouseEvent me){
                                setShow(false);
                                setVisible(false);
                            }
                        };
                        addMouseListener(mia); 
                        addMouseMotionListener(mia);
                        setOpaque(false);
                    }
                    private int x=0, y=0, k=25,z=1;
                    private boolean showDot;
                    public void setShow(boolean b){ this.showDot = b; }
                    private Color color = new Color(0.0f, 0.25f, 1.0f);
                    public void paintComponent(Graphics g) {
                        if (showDot) {
                            g.setColor(color);
                            g.fillOval(x - k, y - k, 2*k, 2*k);
                        }
                    }
                    public void setPos(int x, int y) {
                        int tmpX = this.x, tmpY = this.y; 
                        this.x = x; this.y = y;
                        repaint(tmpX - k , tmpY-k, 2*k+5, 2*k+5);
                        repaint(this.x-k, this.y-k, 2*k+5, 2*k+5);        
                    }
                } );
                frame.getGlassPane().setVisible( true );
                frame.setVisible( true );
            }
        });
    }
}
oliholz
  • 7,447
  • 2
  • 43
  • 82
  • And yeah, notice that you're overriding `paintComponent(...)` instead -- you're welcome. – mre Jun 23 '11 at 12:40