Получение ловушки при закрытии JFrame хоста JPanel без использования события закрытия окна - PullRequest
2 голосов
/ 27 мая 2020

Я понимаю, что формальный способ - это прослушать событие закрытия окна, добавленное к хосту JFrame, а затем просто вызвать метод очистки на JPanel. Однако мне стало любопытно. Лишь недавно у меня возникла потребность в хуке shutdown (закрытие, выход, уход сейчас) в моей Panel, который уже используется в моей программе. И из любопытства я не хотел go повторно посещать места, где эта панель помещается в общий c старый JFrame (тот, который не требует специальной обработки) и где она просто отображается как диалог, ожидая для ввода, после чего будет отклонен. Я не хотел заставлять этот гипотетический другой кодер (я) go добавлять оконных слушателей к тем фреймам, которые уже используют мою панель, требуя от них вызова нового метода выключения. Это мой код в Panel, которому нужна дополнительная возможность, так зачем заставлять всех этих разработчиков go изменять свой код для добавления оконных слушателей? Разве JPanel не может найти это для себя?

Итак, я сделал хак, чтобы JPanel обнаруживал, когда он закрывается, без того, чтобы JFrame нуждался в слушателе окна. JPanel, для меня, теперь более осведомлен о себе ... у него есть собственный tacti c обнаружения закрытия окна, независимо от кодера, использующего мою JPanel, которому нужно писать слушателей и вызывать перехватчики выключения. Я просто хотел попробовать и сделать это без необходимости go назад и изменения существующего кода.

Поймите также, что мой хак работает только с DISPOSE_ON_CLOSE, но ... это то, что я все равно использовал для своего диалога .

Кто-нибудь может мне тогда показать, как мне это делать? Есть ли событие, связанное с недвижимостью, которое я мог бы раскопать? То, что я только что совершил, должно быть огневым. Это должно быть очень неправильно. Тем не менее, у меня есть собственный крючок отключения JPanel, и он работает. Кто-нибудь, пожалуйста, направьте меня в другое место (без очевидного выбора родительского JFrame, использующего собственное событие закрытия окна, я хотел посмотреть, может ли JPanel знать об этом самостоятельно.)

Мой хак работает .. Подскажите, что с этим не так. Это должно быть неправильно, даже если это работает для меня. Верно?

Следующий фрагмент представляет собой работающий макет.

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class JPanelClosing extends JPanel {

    static JFrame frame; // This frame is only here for the mmockup ... what
                         // follows after would be part of your own custom JPanel.

    private boolean formClosing = false;
    private boolean filterEvent = true;

    public JPanelClosing() {
        initComponents();
    }

    private void initComponents() {
        addPropertyChangeListener(new java.beans.PropertyChangeListener() {
            @Override
            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                formPropertyChange(evt);
            }
        });
    }

    private void formPropertyChange(java.beans.PropertyChangeEvent evt) {                                    

        // This is a hack I came up with. The JPanel fires two events when
        // used in a waiting input dialog of an unkown JFrame that hosts it.
        // When the JFrame DefaultCloseOperation is set to DISPOSE_ON_CLOSE:
        // PropertyChangeEvent fires twice when it opens, and twice when it closes.

        // So, I filter out the two events to pick one, like using !valueIsAdjusting.
        // Then, I filter whether it's state one, opening, or state two, closing.
        // This is all kept track of using two field variables; filterEvent, and formClosing 

        // With DISPOSE_ON_CLOSE, (on my machine) I get:
        // Form opened.
        // Form Closed.

        // (EXIT_ON_CLOSE and HIDE_ON_CLOSE will only produce 'Form opened')

        if (!filterEvent) {
            if ( formClosing ) {
                System.out.println("Form Closed.");
                System.exit(0);
            } else {
                formClosing = true;
                System.out.println("Form opened.");
            }
            filterEvent = true;
        } else { // end if value not adjusting
            filterEvent = false;
        }

    }

    public static void main (String args[] ) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                frame = new JFrame();
                final JPanel panel = new JPanelClosing();
                frame.setContentPane(panel);
                frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

Ответы [ 2 ]

3 голосов

Я понятия не имею, является ли это каноническим способом делать что-то, но с AncestorListener вы получите уведомление, если главное окно, содержащее ваш компонент, установлено видимым с помощью вызываемого метода ancestorAdded. Когда это происходит, вы можете получить предка окна через SwingUtilities.getWindowAncestor(...), а затем добавить любых слушателей в это окно. Например:

package pkg1;

import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;

@SuppressWarnings("serial")
public class TestClosing extends JPanel {
    public TestClosing() {
        setPreferredSize(new Dimension(500, 400));

        // addPropertyChangeListener(evt -> System.out.println(evt));

        addAncestorListener(new MyAncestorListener());
    }


    private class MyAncestorListener implements AncestorListener {

        @Override
        public void ancestorAdded(AncestorEvent ae) {
            Window window = SwingUtilities.getWindowAncestor(TestClosing.this);
            if (window != null) {
                window.addWindowListener(new MyWindowListener());
            }
        }

        @Override
        public void ancestorMoved(AncestorEvent ae) {}

        @Override
        public void ancestorRemoved(AncestorEvent ae) {}

    }

    private class MyWindowListener extends WindowAdapter {
        @Override
        public void windowClosed(WindowEvent e) {
            // TODO desired actions
            System.out.println("window closed");
        }

        @Override
        public void windowClosing(WindowEvent e) {
            // TODO desired actions
            System.out.println("window closing");
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        TestClosing mainPanel = new TestClosing();
        JFrame frame = new JFrame("Test Closing");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
}

Я полагаю, ComponentListener, реагирующий на componentShown(ComponentEvent ce), тоже может помочь.

1 голос
/ 27 мая 2020

Вы уверены, хотите делать то, что делаете?

Во-первых, этот код явно не работает. В моей системе «Форма закрыта». никогда не печатается.

Во-вторых, то, что я улавливаю в источнике, - это "layeredContainerLayer" и "ancestor", когда свойства меняются при открытии окна. "ancestor" изменение, вероятно, то, что вы хотите, более или менее, может быть?

Но можете ли вы гарантировать, что только одно "layeredContainerLayer" исходное событие запускается в таком порядке, или что оно происходит только один раз, или что ничто другое не будет распространяться когда-либо? Даже если вы копаете достаточно глубоко, чтобы действительно понять, что происходит и в каком порядке, все равно не делайте этого. Это уродливый хак.

Может быть, реализовать что-нибудь, полагаясь на Runtime.getRuntime().addShutdownHook(...) и ваших собственных слушателей, уведомляющих тех, кто хочет получать уведомления о завершении работы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...