我正在尝试在JScrollPane上绘制一个红色方块。我下面的代码对此做了一个很好的工作,但有时当我滚动视口太快时,红色方块会向上或向下跳跃。
这让我觉得奇怪,因为JScrollPane本身是静止的,所以我假设Swing不会试图移动其中绘制的组件。我猜测实际发生的是红色方块与视口相关联,视口显示移动的图形。
无论如何,如何阻止红色方块跳转并在列表上成功绘制红色方块?也许我完全采取了错误的做法。
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();
}
});
}
}
答案 0 :(得分:6)
JScrollPane应该在 JViewport后面绘制,它应该在列表后面绘制。我猜这只是工作,因为你重写了paint而不是paintComponent,并且一直在JScrollPane上调用repaint,以便在绘制组件后再次绘制自己。
也许您想要使用JLayeredPane并让它持有JScrollPane并在其上绘画。
编辑:或者我现在看到的那个玻璃窗,但我担心如果你这样做,并且将玻璃板设置为可见,你就会失去与底层滚动板交互的能力。
编辑2
例如,
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();
}
});
}
}