Я хочу, чтобы изображение оставалось в верхнем левом углу jscrollpane при прокрутке

У меня есть JPanel в JScrollpane. Я рисую на BufferedImage, который я отображаю на JPanel. В верхнем левом углу JScrollpane мне нужна картинка, которая всегда остается в этом углу, когда я прокручиваю вниз, чтобы увидеть остальную часть моей JPanel. Здесь метод paintComponent Jpanel:

@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    if (bufferedImage != null){
        g.drawImage(bufferedImage, 0, 0, this);
        Point p = parent.getViewPosition();
        System.out.println("paintComponent(): "+ p.x + "," + p.y);
        g.setColor(Color.RED);
        g.fillRect(p.x + 20, p.y + 20, 200, 200);
    }
}

Где parent.getViewPosition() дает мне scrollPane.getViewport().getViewPosition(). Когда я запускаю, я вижу буферизованное изображение с красным прямоугольником в верхнем левом углу. Когда я прокручиваю вниз, я вижу остальную часть буферизованного изображения, но красный прямоугольник перемещается вверх, а затем исчезает и больше не появляется, когда я прокручиваю вверх. В консоли я вижу, что точка p меняется при прокрутке:

paintComponent(): 0,0
paintComponent(): 0,10
paintComponent(): 0,20
paintComponent(): 0,30
paintComponent(): 0,40
paintComponent(): 0,50

Может ли кто-нибудь помочь мне с этой проблемой?


person Jan Van den Bossche    schedule 17.03.2013    source источник
comment
например, используйте paint(), используйте JViewPort.setScrollMode(JViewport.Xxx), используйте собственный RepaintManager, но я бы использовал GlassPane или JLayer   -  person mKorbel    schedule 18.03.2013


Ответы (5)


Вы можете использовать для этого стеклянную панель и указать ей рисовать свое изображение в месте, которое зависит от местоположения окна просмотра. Например:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.*;

@SuppressWarnings("serial")
public class ScrollImgGlass extends JPanel {
   private static final int BI_W = 40;
   private static final int BI_H = BI_W;
   private static final String[] DATA = { "One", "Two", "Three", "Four",
         "Five", "Six", "Seven", "Eight", "Nine", "Zero", "One", "Two",
         "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Zero",
         "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
         "Nine", "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven",
         "Eight", "Nine", "Zero" };
   private BufferedImage img = null;
   private JViewport viewport;

   public ScrollImgGlass(JViewport viewport) {
      setOpaque(false);
      this.viewport = viewport;
      img = new BufferedImage(BI_W, BI_H, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = img.createGraphics();
      g2.setColor(Color.red);
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.fillOval(0, 0, BI_W, BI_H);
      g2.dispose();
   }

   @Override
   protected void paintComponent(Graphics g) {
      Point vpLocation = viewport.getLocationOnScreen();
      Point gpLocation = getLocationOnScreen();

      int x = vpLocation.x - gpLocation.x;
      int y = vpLocation.y - gpLocation.y;

      super.paintComponent(g);
      if (img != null) {
         g.drawImage(img, x, y, this);
      }
   }

   private static void createAndShowGui() {
      JList<String> jList = new JList<String>(DATA);
      jList.setOpaque(false);

      JViewport viewport = new JViewport();
      JScrollPane scrollpane = new JScrollPane();
      scrollpane.setViewport(viewport);
      viewport.setView(jList);

      ScrollImgGlass glass = new ScrollImgGlass(viewport);

      JFrame frame = new JFrame("ScrollImg");
      frame.setGlassPane(glass);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(scrollpane, BorderLayout.CENTER);

      // just to show that this works if the viewport is shifted over
      frame.getContentPane().add(Box.createRigidArea(new Dimension(20, 20)), BorderLayout.NORTH);
      frame.getContentPane().add(Box.createRigidArea(new Dimension(20, 20)), BorderLayout.WEST);

      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      glass.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Что будет отображаться как:

введите здесь описание изображения

person Hovercraft Full Of Eels    schedule 18.03.2013
comment
Я просто хотел бы сказать, что мне нравится JViewport за его обработку Blit ... боль в ... +1 - Также JLayer может работать - person MadProgrammer; 18.03.2013
comment
@MadProgrammer: по какой-то причине я предположил, что окно просмотра упорядочено по оси z над представлением, но, очевидно, я ошибался. Спасибо, что поправили меня раньше. - person Hovercraft Full Of Eels; 18.03.2013
comment
Я играл с переопределением методов paint области просмотра, но из-за того, что панель прокрутки переносит на нее представление, краска обычно не вызывается, если размер области просмотра не изменяется... или что-то в этом роде :P Ваш подход лучше - person MadProgrammer; 18.03.2013
comment
Хорошо, ваше решение работает нормально, я изменил JList на JPanel. - person Jan Van den Bossche; 19.03.2013
comment
Но моя панель прокрутки отображается на JPanel в JTabbedPane. А в JPanel нет метода setGlassPane. Возможно, мне нужно попробовать JLayer, но на данный момент я работаю с java6. - person Jan Van den Bossche; 19.03.2013

Как было предложено MadProgrammer, JLayer работает:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class FixedImageLayerUI extends LayerUI<JComponent>
{
    @Override
    public void paint(Graphics g, JComponent c)
    {
        super.paint(g, c);

        Graphics2D g2 = (Graphics2D) g.create();

        g2.setColor( Color.RED );
        g2.fillOval(0, 0, 10, 10);

        g2.dispose();
    }

    private static void createAndShowUI()
    {
        String[] data =
        {
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
            "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
            "u", "v", "w", "x", "y", "z"
        };

        JList<String> list = new JList<String>( data );
        JScrollPane scrollPane = new JScrollPane( list );

        LayerUI<JComponent> layerUI = new FixedImageLayerUI();
        JLayer<JComponent> layer = new JLayer<JComponent>(scrollPane, layerUI);

        JFrame frame = new JFrame("FixedImage");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( layer );
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

Кроме того, как отметил MadProgrammer, переопределение метода рисования JScrollPane не работает. Однако, если вы сделаете JList непрозрачным, он сработает:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.*;

public class FixedImageScrollPane
{

    private static void createAndShowUI()
    {
        String[] data =
        {
            "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
            "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
            "u", "v", "w", "x", "y", "z"
        };

        JList<String> list = new JList<String>( data );
        list.setOpaque( false );

        JScrollPane scrollPane = new JScrollPane( list )
        {
            @Override
            public void paint(Graphics g)
            {
                super.paint(g);
                g.setColor( Color.RED );
                g.fillOval(0, 0, 10, 10);
            }
        };

        JFrame frame = new JFrame("FixedImage");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( scrollPane );
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}
person camickr    schedule 18.03.2013

Не помещайте изображение в панель, которая прокручивается. Поместите его на другую панель и расположите две панели с помощью менеджера компоновки.

Я предлагаю взглянуть на BorderLayout; у него есть области в точках n, s, e и w и одна в центре, но вам не обязательно использовать их все (все они используются очень редко). Вы можете создать JPanel, установить для его менеджера компоновки значение BorderLayout, поместить панель, содержащую ваше изображение, в ее СЕВЕРНУЮ часть, а панель прокрутки — в ЦЕНТР. В качестве бесплатного бонуса ваша панель JPanel в центре будет растягиваться в обоих измерениях при изменении размера окна, потому что так работает BorderLayout.

person arcy    schedule 17.03.2013

Где-то в вашем коде вы создаете JScrollPane.

Изменять

    final JScrollPane scrollpane = new JScrollPane();

To:

    final JScrollPane scrollpane = new JScrollPane() {
        @Override
        public void paint(final Graphics g) {
            super.paint(g);
            // Put you drawing here...example, draw a geen dot...
            g.setColor(Color.GREEN);
            g.fillOval(0, 0, 30, 30);
        }
    };

РЕДАКТИРОВАТЬ: в комментариях необходимо выполнить setOpaque (false) для объекта, помещенного в JScrollPane.

Пример:

    list.setOpaque(false);
    scrollpane.setViewportView(list);
person Java42    schedule 18.03.2013
comment
-1, хорошая мысль, но она не работает, как заметил MadProgrammer часом ранее. - person camickr; 18.03.2013
comment
@camickr - Вы тестировали? В моих тестах работает нормально. MadP говорит о переопределении отрисовки JViewport, тогда как я переопределяю отрисовку JScrollPane. - person Java42; 18.03.2013
comment
Да, я тестировал его на Windows 7, используя JDK7. Тем не менее, я нашел обходной путь, как указано в моем втором предложении. Опубликуйте код, который вы использовали для тестирования. - person camickr; 18.03.2013
comment
@camickr - Ааа .. Понятно - в моем тестовом примере было установлено setOpaque (false) для объекта, помещенного с помощью setViewportView (), и когда я удалил его ... сбой. - person Java42; 18.03.2013

OK, следующий код работает с JTabbedPane. Мне пришлось добавить компонентный прослушиватель на панель на панели с вкладками, потому что я получаю исключение в методе getLocationOnScreen(), когда панель не отображается на экране.

public class GlassFrame {
    private JPanel panel;
    private JScrollPane scrollPane;
    private BufferesImage img;

    public GlassFrame() {
        panel = new JPanel(){
            @Override
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                img = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2 = img.createGraphics();
                g2.setPaint(Color.WHITE);
                   Rectangle2D rect = new Rectangle2D.Float(0, 0, 420, 420);
                   g2.fill(rect);
                   g2.setPaint(Color.BLACK);
                   for (int i = 0; i <= 10; i++) {
                       g2.draw(new Line2D.Float(10 + i * 40,10,10 + i * 40,410));
                       g2.draw(new Line2D.Float(10,10 + i * 40,410,10 + i * 40));
                   }
                g2.dispose();
                g.drawImage(img, 0, 0, this);
            }
        };
        panel.setPreferredSize(new Dimension(420, 420));
        panel.setOpaque(false);

        scrollPane = new JScrollPane(panel, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
        scrollPane.getVerticalScrollBar().setUnitIncrement(10);

        JFrame frame = new JFrame("ScrollPane and GlassPane");
        final GlassPane glass = new GlassPane(scrollPane.getViewport());
        frame.setGlassPane(glass);

        JTabbedPane tab = new JTabbedPane();
        JPanel panelInTab = new JPanel();
        panelInTab.setLayout(new BorderLayout());
        tab.add("first tab", panelInTab);
        tab.add("second tab", new JPanel());
        panelInTab.add(scrollPane, BorderLayout.CENTER);
        panelInTab.add(new JButton("testbutton"), BorderLayout.NORTH);
        panelInTab.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentShown(ComponentEvent arg0) {
                glass.setVisible(true);
            }
            @Override
            public void componentHidden(ComponentEvent arg0) {
                glass.setVisible(false);
            }
        });

        frame.getContentPane().add(tab);    
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(300, 400));
        frame.pack();
        frame.setLocation(200, 200);
        frame.setVisible(true);
        glass.setVisible(true);
    }

    class GlassPane extends JPanel{
        private JViewport viewport;
        private BufferedImage image = null;

        public GlassPane(JViewport viewport){
            setOpaque(false);
            this.viewport = viewport;
            image = new BufferedImage(58, 58, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = image.createGraphics();
            g2.setColor(Color.red);
            g2.setStroke(new BasicStroke(3.0f));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g2.drawOval(5, 5, 50, 50);
            g2.dispose();
        }

        @Override
        protected void paintComponent(Graphics g) {
            Point vpLocation = viewport.getLocationOnScreen();
            Point gpLocation = getLocationOnScreen();

            int x = vpLocation.x - gpLocation.x;
            int y = vpLocation.y - gpLocation.y;

            super.paintComponent(g);
            if (image != null) {
                g.drawImage(image, x, y, this);
            }
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                GlassFrame frame = new GlassFrame();
            }
        });
    }
}
person Jan Van den Bossche    schedule 25.03.2013