1

I'm using Java Swing for my application, and I want to make use of the JViewport to show a fragment of some canvas-like panel 'behind' the port. But somehow the viewport never positions its view, so there must be something I'm doing. What am I doing wrong, why this code is not working?

The following is an example of what I'm doing on a bigger and more complex scale.

public class MyApp
{
  // THIS IS A TEMPORARY TEST TO GET VIEWPORT WORKING
  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    JViewport viewport = new JViewport();
    viewport.setOpaque(true);
    viewport.setBackground(Color.GRAY);
    frame.add(viewport, BorderLayout.CENTER);
    JPanel canvas = new JPanel(null);
    canvas.setBackground(Color.CYAN);
    viewport.setView(canvas);
    viewport.setViewSize(new Dimension(500, 500));
    viewport.setExtentSize(new Dimension(300, 300));
    viewport.setPreferredSize(new Dimension(300, 300));

    // item one
    JLabel label = new JLabel("This is a 32x32 box");
    label.setOpaque(true);
    label.setBackground(Color.GREEN);
    label.setIcon(Sprites.BOX2.getSprite());
    label.setBounds(0, 0, 200, 32); // position upper left, 200 wide
    label.setHorizontalAlignment(SwingConstants.LEFT);
    canvas.add(label);

    // item two
    JLabel label2 = new JLabel("This is a 32x32 wall");
    label2.setOpaque(true);
    label2.setBackground(Color.ORANGE);
    label2.setIcon(Sprites.WALL1.getSprite());
    label2.setBounds(300, 468, 200, 32); // position lower right, 200 wide
    label2.setHorizontalAlignment(SwingConstants.RIGHT);
    label2.setHorizontalTextPosition(SwingConstants.LEFT);
    canvas.add(label2);

    // this should scroll the canvas to the left and up, so the box becomes invisible and the wall visible
    viewport.setViewPosition(new Point(200, 200));  

    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }
}

The result is that the 'canvas' is simply sticking at 0,0, it never moves to 200,200 like I do with setViewPostition(). It's contents is perfectly positioned well, regardless of the null layout manager. I just wrote the canvas to be a JPanel for simplicity, but it's really a complex JLayeredPane.

animuson
  • 53,861
  • 28
  • 137
  • 147
  • See also [`ScrollPanePaint`](http://stackoverflow.com/a/3518047/230513), [_et al_](http://stackoverflow.com/a/10097538/230513). – trashgod Apr 18 '13 at 15:51

1 Answers1

4

There are several problems in your case:

  • You use null-layout/absolute positionning: stop doing that... forever. It's a bad practice, a very bad and nasty habit which always leads to the same point: tons of problems.
  • Don't call setPreferredSize(): either use an appropriate LayoutManager or override getPreferredSize()
  • Consider implementing Scrollable for your canvas and where getPreferredScrollableViewportSize() would return new Dimension(300, 300) (the targetted viewport extent size)
  • Calling setSize()/setExtentSize() is pretty much useless because this will be overriden by LayoutManager.
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • +1 last point could be - use JLayer (Java7) instead of JViewport – mKorbel Apr 18 '13 at 13:39
  • 1) I understand layout managers are better than positioning, but that's not being really the problem. The contents of the canvas are perfect, but the canvas itself isn't positioned correctly. 2&4) Do you mean I have to add a layout manager to the viewport? I thought it didn't need one. 3) I will look into making the canvas **Scrollable**. – Jack McKalling Apr 18 '13 at 15:17
  • @JackMcKalling The viewport has its own LayoutManager. The preferred size of a viewport is based on 1) if the viewport view implements Scrollable, the value returned by getPreferredScrollableViewPortSize 2) else, the preferred size. The viewport view gets a size defined by the value returned by getPreferredSize() – Guillaume Polet Apr 18 '13 at 15:37
  • @Guillaume Polet Ok, I understand now. I removed the setViewSize(), setExtendSize() and setPreferredSize() calls, made the canvas implement **Scrollable** and let it return canvas and viewport size by overriding getPreferredSize() and getPreferredScrollableViewportSize() respectively, and now it works! Thanks a lot! – Jack McKalling Apr 18 '13 at 15:47
  • After "some time" and a larger monitor, I just discovered that the problem sort of reappears if the canvas becomes smaller than the viewport's extent size. The viewport refuses to offset the view, it always keeps sticking to whichever side is closest to the requested view position. In my case it only happens horizontally because the view only fits within the extent size horizontally. But this is a real pain, I want the view to be centered no matter how big it is. – Jack McKalling Mar 04 '22 at 10:23
  • Found the cause of that. ViewportLayout says this: `/* If the new viewport size would leave empty space to the right of the view, right justify the view or left justify the view when the width of the view is smaller than the container. */` – Jack McKalling Mar 04 '22 at 12:23