How to set hard limit for JComponent when setMaximumSize () and setPrefferedSize () do not work?

I am trying to create an image processing frame similar to the one found in Photoshop or Paint Shop Pro, and I am having problems.

Now I have a JFrame window with JDesktopPane. When I click the button, a JInternalFrame is created with the following components:

imageLabel = new JLabel("picture.png"); scrollPane.setViewPort(imageLabel); internalFrame.add(scrollPane); // I also tried with a BorderLayout() desktopPane.add(internalFrame); 

My problem is this: I don't want JLabel or JScrollPane to stretch to the size of a JInternalFrame if the JLabel is smaller than the JInternalFrame.

I tried to fill the space around JLabel with "empty" JLabels. I tried switching the layout styles of JScrollPane. I tried setting the preferred and maximum sizes of JLabel and JScrollPane, as for picture.png. None of this works for what I need. I don’t want the empty “space” around the JLabel to be part of a JScrollPane or JLabel, so I can use different MouseEvents to trigger the image itself, and not the space left by the “stretched” JLabel or JScrollPane every time I resize the JInternalFrame.

Thanks in advance.

Edit1: Here is some code that emphasizes the problem.

 import java.awt.*; import java.awt.event.*; class fooFrame extends JFrame implements MouseListener { private static fooFrame frame; JLabel fooLabel; public fooFrame() { JDesktopPane background = new JDesktopPane(); JInternalFrame internalFrame = new JInternalFrame("Internal Frame", true, true, true, true); internalFrame.setSize(500, 500); internalFrame.setLocation(20, 20); internalFrame.setVisible(true); Image image = Toolkit.getDefaultToolkit().getImage("test.gif"); fooLabel = new JLabel(new ImageIcon("test.gif")); fooLabel.setPreferredSize(new Dimension(image.getWidth(null), image.getHeight(null))); JScrollPane fooScrollPane = new JScrollPane(fooLabel, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); fooScrollPane.setPreferredSize(new Dimension(fooLabel.getWidth(), fooLabel.getHeight())); fooScrollPane.setViewportView(fooLabel); // add JLabel to JScrollPane internalFrame.add(fooScrollPane); // add JScrollPane to JInternalFrame background.add(internalFrame); // add JInternalFrame to JDesktopPanel this.setContentPane(background); // add JDesktopPanel to JFrame fooLabel.addMouseListener(this); } public void mouseClicked(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Clicked the picture."); } public void mouseEntered(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Entered the picture."); } public void mouseExited(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Exited the picture."); } public void mousePressed(MouseEvent me){} public void mouseReleased(MouseEvent me){} public static void createAndShowGUI() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { } frame = new fooFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("foo"); frame.setSize(800,600); frame.setLocationRelativeTo(null); frame.setVisible(true); } } 

You will need to get your own "test.gif", and if you make the inner frame larger than the picture, it will fill the remaining space with a label. Because all mouseEvents fire when I cross the inner frame, not the image, as if I want this to happen.

Edit2: Code changed with Kleopatra suggestions.

 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.LineBorder; public class a1 { public static void main(String[] args) { fooFrame.createAndShowGUI(); } } class fooFrame extends JFrame implements MouseListener { private static fooFrame frame; JLabel fooLabel; public fooFrame() { JDesktopPane background = new JDesktopPane(); JInternalFrame internalFrame = new JInternalFrame("Internal Frame", true, true, true, true); internalFrame.setLocation(20, 20); internalFrame.setVisible(true); internalFrame.pack(); Image image = Toolkit.getDefaultToolkit().getImage("test.gif"); fooLabel = new JLabel(new ImageIcon(image)); fooLabel.setBorder(new LineBorder(Color.PINK)); JScrollPane fooScrollPane = new JScrollPane((fooLabel), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); internalFrame.setLayout(new BoxLayout(internalFrame.getContentPane(), BoxLayout.LINE_AXIS)); fooScrollPane.setViewportView(fooLabel); // add JLabel to JScrollPane internalFrame.add(fooScrollPane); // add JScrollPane to JInternalFrame background.add(internalFrame); // add JInternalFrame to JDesktopPanel this.setContentPane(background); // add JDesktopPanel to JFrame fooLabel.addMouseListener(this); } public void mouseClicked(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Clicked the picture."); } public void mouseEntered(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Entered the picture."); } public void mouseExited(MouseEvent me) { if (me.getSource() == fooLabel) System.out.println("Exited the picture."); } public void mousePressed(MouseEvent me) { } public void mouseReleased(MouseEvent me) { } @Override public Dimension getMaximumSize() { return getPreferredSize(); } public static void createAndShowGUI() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { } frame = new fooFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("foo"); frame.setSize(800, 600); frame.setLocationRelativeTo(null); frame.setVisible(true); } } 
+2
source share
2 answers

In this example, the inner frame will initially contain no more than MAX_SIZE pixels. Smaller shots will be sized to prevent scrolling.

Addendum: read the header more carefully, you can also limit the maximum size of the inner frame:

 internalFrame.setMaximumSize(new Dimension(fooLabel.getPreferredSize())); 

Appendix: This option may help clarify the problem. With the default layout BorderLayout.CENTER , scrollbars appear as needed, but MouseListener does not behave as required. With a layout that relates to preferred sizes, FlowLayout , MouseListener reports MouseListener displayed correctly, but scrollbars never appear, since the preferred label size never changes. I added synthetic images for convenience.

enter image description here

 import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import javax.swing.*; class PictureFrame extends JFrame { private static final String NAME = "image.jpg"; public PictureFrame() { JDesktopPane dtp = new JDesktopPane(); dtp.add(new MyFrame("Large", FauxImage.create(300, 500), 50)); dtp.add(new MyFrame("Small", FauxImage.create(200, 200), 25)); this.add(dtp); } private static class MyFrame extends JInternalFrame { private static final int MAX_SIZE = 256; public MyFrame(String title, Image image, int offset) { super(title, true, true, true, true); //this.setLayout(new FlowLayout()); final JLabel label = new JLabel(new ImageIcon(image)); this.add(new JScrollPane(label)); this.pack(); int w = Math.min(MAX_SIZE, image.getWidth(null)); int h = Math.min(MAX_SIZE, image.getHeight(null)); Insets i = this.getInsets(); this.setSize(w + i.left + i.right, h + i.top + i.bottom); this.setLocation(offset, offset); this.setVisible(true); label.addMouseListener(new MouseAdapter() { @Override public void mouseEntered(MouseEvent me) { if (me.getSource() == label) { System.out.println("Entered."); } } @Override public void mouseExited(MouseEvent me) { if (me.getSource() == label) { System.out.println("Exited."); } } }); } } private static class FauxImage { static public Image create(int w, int h) { BufferedImage bi = new BufferedImage( w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = bi.createGraphics(); g2d.setPaint(Color.lightGray); g2d.fillRect(0, 0, w, h); g2d.setColor(Color.black); String s = w + "\u00D7" + h; int x = (w - g2d.getFontMetrics().stringWidth(s)) / 2; g2d.drawString(s, x, 24); g2d.dispose(); return bi; } } public static void createAndShowGUI() { PictureFrame frame = new PictureFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("PictureFrame"); frame.setSize(640, 400); frame.setLocationRelativeTo(null); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } } 
+2
source

The answer to the problem layout is LayoutManager. Always. It never installs XXSize (although nothing is absolutely absolute :-)

Just to be sure that I understand what you are after:

  • always has a label in the size of pref (the default is the size of its icon)
  • allow scrolling if the internal frame is smaller than the prefix, i.e. the scroll size is reduced to a smaller value, the label remains on the prefix
  • prevent correction of scrollPane and its contents if the inner frame is larger than pref

An important LayoutManager in this scenario is the one that controls the size of the scrollpane: this is the LayoutManager of the inner content area. The default dispatcher is BorderLayout: to determine the size of the center in any free space. We need to replace this with the one that matches max, and make the center component (scrollpane) for the message max (usually Short.MAX)

  internalFrame.setLayout(new BoxLayout(internalFrame.getContentPane(), BoxLayout.LINE_AXIS)); Image image = Toolkit.getDefaultToolkit().getImage("test.gif"); fooLabel = new JLabel(new ImageIcon(image)); JScrollPane fooScrollPane = new JScrollPane(fooLabel), JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) { @Override public Dimension getMaximumSize() { return getPreferredSize(); } }; internalFrame.add(fooScrollPane); // add JScrollPane to JInternalFrame 
+1
source

All Articles