【问题标题】:Drawing a static image over the viewport of a JScrollPane在 JScrollPane 的视口上绘制静态图像
【发布时间】:2011-08-11 21:11:13
【问题描述】:

我正在尝试在 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();
      }
    });
  }
}

【问题讨论】:

  • 这个解决方案可能太健壮了,但您可以尝试使用Glass Pane。例如 - stackoverflow.com/questions/6609888/… 但我可能错了,这个解决方案可能不适用。
  • @masson:请参阅我的回答编辑

标签: java swing jscrollpane


【解决方案1】:

JScrollPane 应该在 behind 绘制 JViewport,而 JViewport 应该在 behind 列表中绘制。我猜这只是工作,因为你覆盖paint 而不是paintComponent 并且一直在JScrollPane 上调用repaint,以便它在其组件被绘制后再次绘制自己。

也许您想使用 JLayeredPane 并让它持有 JScrollPane,并在其上绘画。

编辑:或者我现在看到的 mre 建议的玻璃窗格,但我担心如果你这样做,并将玻璃窗格设置为可见,你将失去与底层滚动窗格交互的能力。

编辑 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();
         }
      });
   }
}

【讨论】:

  • 我不确定您的编辑是否属实。在那个例子中,我故意阻止所有输入事件,Glass Pane 确实拦截了它们,但我不确定它是否在默认情况下完全阻止它们。因此,与底层 scollpane 交互的能力可能仍然存在,但我还没有测试过这个! :D
  • 值得测试。 :D
  • @masson 查看有关在 JViewPort stackoverflow.com/questions/7006522/… 上绘制任何内容的相关帖子,并查看由垃圾神 +1 提供的链接
  • @mKorbel:请解释一下“(你的表格不是 Old.Forums.Sun”吗?
  • 这个很好的例子我在你的小 E.... 来自 forums.sun.com 的加密名称不是你
猜你喜欢
  • 2014-07-24
  • 2010-09-07
  • 1970-01-01
  • 2013-11-15
  • 2013-12-13
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
相关资源
最近更新 更多