Любопытное поведение java.awt.Component, setVisible(), LayoutManager

#java #awt

#java #awt

Вопрос:

Я пытался создать графический интерфейс и обнаружил любопытное поведение java.awt.Component. Я хотел setVisible(true) удалить setVisible(false) java.awt.Component с помощью внешнего события. Но это работает только тогда, когда подлежащий переключению компонент уже был виден в первую очередь. В приложении я предоставил минимальную копию моей проблемы.

 package test;

import java.awt.Button;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public final class Test {
    public static void main(String[] args) {
        Button testButton = new Button("Test Button");
        testButton.setVisible(false); /* Removing this line makes the code to work as intended, but why? */
        
        Frame mainFrame = new Frame("Test");
        mainFrame.setMinimumSize(new Dimension(640, 480));
        mainFrame.setVisible(true);
        mainFrame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            }
        });
        
        mainFrame.add(testButton);
        
        mainFrame.setMenuBar(new MenuBar());
        mainFrame.getMenuBar().add(new Menu("Test"));
        mainFrame.getMenuBar().getMenu(0).add(new MenuItem("Show/Hide "Test Button""));
        mainFrame.getMenuBar().getMenu(0).getItem(0).addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                if(testButton.isVisible()) testButton.setVisible(false);
                else testButton.setVisible(true);
                System.out.println("testButton.getBounds()"   testButton.getBounds()); /* EDIT: Added this line for debugging. */
                System.out.println("testButton.isVisible() "   testButton.isVisible());
            }
        });
    }
}
  

Когда вы удаляете строку testButton.setVisible(false); , testButton доступен для просмотра, а также переключается в состоянии видимости, в противном случае нет. Кто-нибудь знает почему? Может быть, layout-manager не работает с невидимыми компонентами?

РЕДАКТИРОВАТЬ: кажется, что layout-manager не setBounds(...) работает с невидимыми компонентами, но почему?

Комментарии:

1. Вызов mainFrame.invalidate(); после изменения видимости testButton может помочь.

2. Извините, но вызов mainFrame.invalidate(); , похоже, не помогает.

Ответ №1:

Может быть, layout-manager не работает с невидимыми компонентами?

Правильно, это зависит от правил менеджера компоновки.

Например, FlowLayout и BoxLayout игнорируют невидимые компоненты при выполнении макета. Однако в GridLayout будет зарезервировано место.

При использовании Swing изменение свойства компонента должно автоматически вызывать диспетчер компоновки. Таким образом, вызов setVisible() должен вызвать новый макет.

Однако, если компоновка не выполняется автоматически, вы должны использовать код типа:

 testButton.getParent().revalidate();
  

Я не использовал AWT уже более десяти лет, но, насколько я помню, AWT не такой умный, как Swing, поэтому вам нужно использовать:

 testButton.getParent().validate();
  

после установки видимого состояния кнопки. По крайней мере, это работает в предоставленном вами MRE.

Комментарии:

1. Это сработало, спасибо. Я также нашел второе решение своей проблемы. При testButton.setVisible(false); первом вызове после добавления кнопки с mainFrame.add(testButton); помощью , проблема не возникает. Жаль, что это поведение не задокументировано в официальной документации Java API, или, по крайней мере, я ничего не нашел по этой теме.