Как установить жесткое ограничение на JComponent, когда setMaximumSize() и setPrefferedSize() не работают?

Я пытаюсь сделать кадр обработки изображения похожим на тот, который есть в Photoshop или Paint Shop Pro, и у меня возникают проблемы.

Прямо сейчас у меня есть окно JFrame с JDesktopPane. Когда я нажимаю кнопку, создается JInternalFrame со следующими компонентами:

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

Моя проблема заключается в следующем: я не хочу, чтобы JLabel или JScrollPane растягивались до размера JInternalFrame, если JLabel меньше, чем JInternalFrame.

Я попытался заполнить пространство вокруг JLabel "пустыми" JLabels. Я попытался переключить стили макета JScrollPane. Я попытался установить предпочтительные и максимальные размеры JLabel и JScrollPane для изображения picture.png. Ничто из этого не работает для того, что мне нужно. Я не хочу, чтобы пустое "пространство" вокруг JLabel было частью JScrollPane или JLabel, чтобы я мог использовать различные MouseEvents для запуска самого изображения, а не пространство, оставленное "растянутыми" JLabel или JScrollPane всякий раз, когда Я изменяю размер JInternalFrame.

Заранее спасибо.

Edit1: здесь немного кода, который выдвигает на первый план проблему.

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

Вам нужно будет получить свой собственный "test.gif", и если вы сделаете внутренний фрейм больше, чем картинка, он заполнит оставшееся пространство меткой. Поскольку все события mouseEnt запускаются, когда я пересекаю внутренний фрейм, а не на изображение, как я хочу, чтобы это произошло.

Edit2: код изменен с предложениями Клеопатры.

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 ответа

В этом примере внутренняя рамка изначально будет не более MAX_SIZE пиксели. Меньшие изображения будут иметь размер, так что прокрутка не требуется.

Приложение: Читая заголовок более внимательно, вы также можете ограничить максимальный размер внутреннего фрейма:

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

Приложение: этот вариант может помочь прояснить проблему. С макетом по умолчанию, BorderLayout.CENTERполосы прокрутки отображаются по мере необходимости, но MouseListener не ведет себя так, как хотелось бы. С макетом, который учитывает предпочтительные размеры, FlowLayout, MouseListener сообщает правильно, но полосы прокрутки никогда не появляются, так как предпочтительный размер метки никогда не меняется. Я добавил синтетические изображения для удобства.

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

Ответом на проблемы с макетом является LayoutManager. Всегда. Он никогда не устанавливает XXSize (хотя ничто не является абсолютно абсолютным:-)

Просто чтобы убедиться, что я понимаю, что вы после:

  • всегда иметь метку в префе размер (по умолчанию размер ее иконки)
  • разрешить прокрутку, если internalFrame меньше, чем pref, то есть размер полосы прокрутки уменьшается до меньшего, метка остается на своем префе
  • запретить выравнивание scrollPane и его содержимого, если внутренний фрейм больше, чем pref

Важным LayoutManager в этом сценарии является тот, который управляет размером полосы прокрутки: это LayoutManager для contentPane internalFrame. Менеджер по умолчанию - BorderLayout: размер его центра зависит от доступного пространства. Нам нужно заменить его на тот, который учитывает максимум, и заставить центральный компонент (полосу прокрутки) сообщать о максимуме (обычно это 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
Другие вопросы по тегам