I want a picture to stay in top left corner of a jscrollpane while scrolling
Asked Answered
O

5

5

I have a JPanel in a JScrollpane. I draw on a BufferedImage, which I display on the JPanel. In the top left corner of the JScrollpane, I want a picture, that always stays in that corner when I scroll down to see the rest of my JPanel. Here the paintComponent method of the 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);
    }
}

Where parent.getViewPosition() give me the scrollPane.getViewport().getViewPosition(). When I start up, I can see the buffered image with the red rectangle in the top left corner. When I scroll down, I can see the rest of the buffered image, but the red rectangle moves up and then disappaeres and don't come again when I scroll up. In the console I can see that point p changes when I scroll:

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

Can anyone help me with this problem?

Ob answered 17/3, 2013 at 22:49 Comment(1)
for example, use paint(), use JViewPort.setScrollMode(JViewport.Xxx), use own RepaintManager, but I'd be use GlassPane or JLayerUnmeriting
M
4

You could use a glass pane for this and tell it to draw its image at a location that depends on the location of the viewport. For example:

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

Which would display like:

enter image description here

Meade answered 18/3, 2013 at 0:8 Comment(5)
I'd just like to say, I love the JViewport for it's Blit processing...pain in the ... +1 - Also JLayer might workJazminejazz
@MadProgrammer: for some reason, I had assumed that the viewport is z-ordered above the view, but obviously I was wrong. Thanks for correcting me earlier.Meade
I've been playing around with overriding the paint methods of the viewport, but because of the way that the scrollpane "blits" the view onto it, paint generally doesn't get called unless the size of the viewport changes ... or something like that :P Your approach is betterJazminejazz
Ok, your solution works fine, I changed the JList into a JPanel.Ob
But my scrollpane is displayed on a JPanel in a JTabbedPane. And JPanel has no setGlassPane method. Maybe I have to try the JLayer, but for the moment I'm working with java6.Ob
B
4

As suggested by MadProgrammer, a JLayer does work:

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

Also, as noted by MadProgrammer, overriding the paint method of JScrollPane doesn't work. However if you make the JList non-opaque it does work:

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();
            }
        });
    }
}
Bleb answered 18/3, 2013 at 2:36 Comment(0)
P
1

Don't put the picture in the panel that scrolls. Put it in a different panel, and arrange the two panels with a layout manager.

I suggest looking at BorderLayout; it has areas at n, s, e, and w, and one in center, but you don't have to use all of them (very seldom are all of them used). You can create a JPanel, set its layout manager to BorderLayout, put the panel containing your image in the NORTH part of that, and put your scrolling panel in the CENTER. As a no-cost bonus, your JPanel in the center will stretch in both dimensions when/if the window is resized, because that's the way BorderLayout works.

Protactinium answered 17/3, 2013 at 23:42 Comment(0)
A
1

Somewhere in your code you create a JScrollPane.

Change

    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);
        }
    };

EDIT: Per comments, need do a setOpaque(false) on the object placed in the JScrollPane.

Example:

    list.setOpaque(false);
    scrollpane.setViewportView(list);
Aldarcie answered 18/3, 2013 at 1:17 Comment(4)
-1, good thought but it doesn't work as has been noted by MadProgrammer an hour earlier.Bleb
@Bleb - Did you test? In my tests, it works fine. MadP talks about overriding JViewport paint, whereas I override JScrollPane paint.Aldarcie
Yes, I tested it on Windows 7, using JDK7. However, I did find a workaround as noted in my second suggestion. Post the code you used to test it.Bleb
@Bleb - Ahh..I see - my testcase had setOpaque(false) on the object placed with setViewportView() and when I removed it...failure.Aldarcie
O
0

OK, following code works with a JTabbedPane. I had to add a componentListener to the panel in the tabbed pane, because I get an exception on the method 'getLocationOnScreen()' when the pane is not on the screen.

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();
            }
        });
    }
}
Ob answered 25/3, 2013 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.