2011-08-11 1 views
4

J'essaie de dessiner un carré rouge sur un JScrollPane. Le code que j'ai ci-dessous fait un bon travail de ceci, mais parfois quand je fais défiler la fenêtre trop vite, le carré rouge saute vers le haut ou vers le bas.Dessiner une image statique sur la fenêtre d'un JScrollPane

enter image description here

Cela m'a frappé comme étrange depuis le JScrollPane lui-même est stationnaire, donc je suppose swing ne tenterait pas de déplacer les pièces peintes à l'intérieur. J'imagine que ce qui se passe réellement, c'est que le carré rouge est associé à la fenêtre d'affichage, qui affiche les graphiques qui bougent.

Quoi qu'il en soit, comment puis-je empêcher le carré rouge de sauter et dessiner avec succès un carré rouge sur la liste? Peut-être que je prends la mauvaise approche tout à fait.

package components; 

import java.awt.*; 
import java.util.Vector; 

import javax.swing.*; 
import javax.swing.event.*; 

@SuppressWarnings("serial") 
public class DialogWithScrollPane extends JFrame { 

    public DialogWithScrollPane() { 
    super(); 

    setResizable(false); 
    Container pane = getContentPane(); 

    Vector<Object> listOfStuff = new Vector<Object>(); 
    for (int i = 0; i < 100; i++) { 
     listOfStuff.add(Integer.toString(i)); 
    } 

    final JScrollPane scrollPane = new JScrollPane() { 

     public void paint(Graphics g) { 
     System.out.println("JScrollPane.paint() called."); 
     super.paint(g); 

     g.setColor(Color.red); 
     g.fillRect(20, 50, 100, 200); 
     } 
    }; 
    JList list = new JList(listOfStuff) { 
     public void paint(Graphics g) { 
     System.out.println("JList.paint() called."); 
     super.paint(g); 

     // Well, I could do this... 
      // 
     // scrollPane.repaint(); 
     // 
     // ...and it would solve the problem, but it would also result in an 
     // infinite recursion since JScrollPane.paint() would call this 
     // function again. 
     } 
    }; 

    // Repaint the JScrollPane any time the viewport is moved or an item in the 
    // list is selected. 
    scrollPane.getViewport().addChangeListener(new ChangeListener() { 
     public void stateChanged(ChangeEvent e) { 
     scrollPane.repaint(); 
     } 
    }); 
    list.addListSelectionListener(new ListSelectionListener() { 
     public void valueChanged(ListSelectionEvent e) { 
     scrollPane.repaint(); 
     } 
    }); 

    scrollPane.setViewportView(list); 

    pane.add(scrollPane); 
    setMinimumSize(new Dimension(300, 300)); 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    setLocation(500, 250); 
    setVisible(true); 
    } 

    public static void main(String[] args) { 
    javax.swing.SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
     new DialogWithScrollPane(); 
     } 
    }); 
    } 
} 
+2

Cette solution pourrait être trop robuste pour cela, mais vous pouvez essayer de dessiner sur le verre 'Pane'. Par exemple - http://stackoverflow.com/questions/6609888/drawing-between-2-images-in-1-jpanel/6610064#6610064 Mais je peux me tromper et cette solution peut ne pas être applicable. – mre

+0

@masson: s'il vous plaît voir modifier à ma réponse –

Répondre

6

Le JScrollPane doit être peint derrière la JViewport qui devrait être la peinture derrière la liste. J'imagine que cela ne fonctionne que parce que vous surchargez la peinture et non paintComponent et que vous appelez repaint sur le JScrollPane tout le temps pour qu'il se répète après que ses composants soient peints.

Peut-être que vous voulez utiliser un JLayeredPane et le faire tenir le JScrollPane, et peindre dessus. : Ou le glasspane comme je le vois maintenant que mre suggère, mais j'ai peur que si vous faites cela, et mettez le glasspane visible, vous perdrez la capacité d'interagir avec le scrollpane sous-jacent.

Edit 2
Pour exemple,

import java.awt.*; 
import java.awt.event.ComponentAdapter; 
import java.awt.event.ComponentEvent; 
import java.util.Vector; 
import javax.swing.*; 

@SuppressWarnings("serial") 
public class DialogWithScrollPane2 extends JFrame { 

    public DialogWithScrollPane2() { 
     super(); 

     //setResizable(false); 
     final JPanel pane = (JPanel) getContentPane(); 

     Vector<Object> listOfStuff = new Vector<Object>(); 
     for (int i = 0; i < 100; i++) { 
     listOfStuff.add(Integer.toString(i)); 
     } 

     final JScrollPane scrollPane = new JScrollPane(); 
     JList list = new JList(listOfStuff); 

     scrollPane.setViewportView(list); 

     final JPanel blueRectPanel = new JPanel() { 
     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      g.setColor(Color.blue); 
      g.fillRect(20, 50, 100, 200); 
     } 
     }; 
     blueRectPanel.setOpaque(false); 

     final JLayeredPane layeredPane = new JLayeredPane(); 
     layeredPane.add(scrollPane, JLayeredPane.DEFAULT_LAYER); 
     layeredPane.add(blueRectPanel, JLayeredPane.PALETTE_LAYER); 

     layeredPane.addComponentListener(new ComponentAdapter() { 

     private void resizeLayers() { 
      final JViewport viewport = scrollPane.getViewport(); 
      scrollPane.setBounds(layeredPane.getBounds()); 
      blueRectPanel.setBounds(viewport.getBounds()); 
      SwingUtilities.invokeLater(new Runnable() { 
       public void run() { 
        blueRectPanel.setBounds(viewport.getBounds()); 
       } 
      }); 
     } 

     @Override 
     public void componentShown(ComponentEvent e) { 
      resizeLayers(); 
     } 

     @Override 
     public void componentResized(ComponentEvent e) { 
      resizeLayers(); 
     } 
     }); 

     pane.add(layeredPane); 
     setPreferredSize(new Dimension(300, 300)); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     pack(); 
     setLocation(500, 250); 
     setVisible(true); 
    } 

    public static void main(String[] args) { 
     javax.swing.SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      new DialogWithScrollPane2(); 
     } 
     }); 
    } 
} 
+1

Je ne suis pas sûr que votre modification est vraie. Dans cet exemple, je bloque intentionnellement tous les événements d'entrée et le 'Glass Pane' les intercepte, mais je ne suis pas sûr qu'il les bloque entièrement par défaut. Donc, la possibilité d'interagir avec le scollpane sous-jacent peut encore être là, mais je n'ai pas testé cela! : D – mre

+0

Cela vaudrait la peine d'être testé. : D –

+1

@masson voir rellevant post à propos de la peinture sur JViewPort http://stackoverflow.com/questions/7006522/how-to-add-background-image-in-jtable-when-printing/7006684#7006684 et voir les liens par trashgod +1 – mKorbel