Drawing a static image in the JScrollPane viewport

I am trying to draw a red square over a JScrollPane. The code that I have below does it fine, but sometimes when I scroll the viewport too quickly, the red square jumps up or down.

enter image description here

It seemed strange to me, since the JScrollPane itself is motionless, so I assumed that Swing would not try to move the components painted inside it. I assume that what actually happens is that the red square is associated with a viewport displaying graphics that are moving.

Anyway, how can I prevent the red square from jumping and successfully draw a red list above the list? Maybe I'm wrong at all.

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(); } }); } } 
+4
source share
1 answer

JScrollPane should draw for JViewport, which should be painted over behind the list. I assume this only works because you redefine the paint rather than paintComponent and constantly rewrite it to JScrollPane so that it is drawn again after its components have been painted.

Perhaps you want to use JLayeredPane and hold JScrollPane and draw on it.

edit: or glass glass, as I now see what mre offers, but I'm afraid if you do this and set the viewfinder to visible, you will lose the ability to interact with the underlying scrolling.

Edit 2
For instance,

 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(); } }); } } 
+6
source

All Articles