Закрытие модального JInternalFrame
Я следовал подходу 2 этого руководства, поэтому теперь у меня есть ModalInternalFrame, который блокирует ввод всех остальных фреймов, как я и хотел. Тем не менее, я сделал одно изменение из примера, и теперь у меня есть две проблемы.
Изменение
Я удалил JOptionPane, потому что весь смысл в том, чтобы показать мою собственную панель. Чтобы закрыть его, я установил closeable
в true
и добавил InternalFrameListener с тем же кодом, что и слушатель примера для JOptionPane. Это не работает, поэтому я также добавил код в конце doDefaultCloseAction.
Проблемы
- Рамка ModalInternal никогда не исчезает. Я думаю, что какое-то исключение выбрасывается, но...
- Я не вижу никаких исключений и не знаю, куда они идут. Обычно в режиме отладки Eclipse останавливается непосредственно перед тем, как исключение передается UncaughtExceptionHandler, но в этом случае этого не происходит.
Код
Если мое описание проблемы не помогает, вот моя версия ModalInternalFrame. Если вы хотите больше кода, я также могу опубликовать это. Извините, это так долго, но я постарался сделать его максимально сжатым.
public class ModalInternalFrame extends JInternalFrame {
public ModalInternalFrame(String title, JRootPane rootPane,
Component desktop) {
super(title, false, true, false, false);
// create opaque glass pane
final JPanel glass = new JPanel();
glass.setOpaque(false);
// Attach mouse listeners
MouseInputAdapter adapter = new MouseInputAdapter() { };
glass.addMouseListener(adapter);
glass.addMouseMotionListener(adapter);
this.addInternalFrameListener(new InternalFrameListenerAdapter() {
public void internalFrameClosed(InternalFrameEvent e) { close(); }
public void internalFrameClosing(InternalFrameEvent e){ close(); }
});
// Change frame border
putClientProperty("JInternalFrame.frameType", "optionDialog");
// Size frame
Dimension size = getPreferredSize();
Dimension rootSize = desktop.getSize();
setBounds((rootSize.width - size.width) / 2,
(rootSize.height - size.height) / 2, size.width, size.height);
desktop.validate();
try { setSelected(true); }
catch (PropertyVetoException ignored) { }
glass.add(this); // Add modal internal frame to glass pane
rootPane.setGlassPane(glass); // Change glass pane to our panel
glass.setVisible(true); // Show glass pane, then modal dialog
}
private void close(){
if (isVisible()) {
try { setClosed(true); }
catch (PropertyVetoException ignored) { }
setVisible(false);
rootPane.getGlassPane().setVisible(false);
}
}
@Override public void doDefaultCloseAction() {
super.doDefaultCloseAction();
close();
}
@Override public void setVisible(boolean flag) {
super.setVisible(flag);
if (flag) startModal();
else stopModal();
}
private synchronized void startModal() {
try {
if (SwingUtilities.isEventDispatchThread()) {
EventQueue theQueue = getToolkit().getSystemEventQueue();
while (isVisible()) {
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
if (event instanceof ActiveEvent) {
((ActiveEvent) event).dispatch();
} else if (source instanceof Component) {
((Component) source).dispatchEvent(event);
} else if (source instanceof MenuComponent) {
((MenuComponent) source).dispatchEvent(event);
} else {
System.err.println("Unable to dispatch: " + event);
}
}
} else { while (isVisible()) { wait(); } }
} catch (InterruptedException ignored) {
}
}
private synchronized void stopModal() { notifyAll(); }
}
Обновление: я обнаружил, что модальные диалоговые окна вполне соответствуют моим потребностям, но если у кого-то есть идея, я буду рад ее услышать. Одна вещь, которую я не пробовал - это обернуть каждый метод в try {} catch (Exception e){}, что, вероятно, очень поможет.
5 ответов
Попробуй это. Я узнал об этом из сообщения в блоге Webby IT на JInternal Frames: http://webbyit.blogspot.com/2011/03/managing-jinternalframes-within.html
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
/**
* An extended <code>JInternalFrame</code> that provides modality in a child/parent hierarchy.
* <a href="From http://webbyit.blogspot.com/2011/03/managing-jinternalframes-within.html">source: webby it internal frames blog post</a>
*
* @author webbyit
*/
public class ModalityInternalFrame extends JInternalFrame {
protected JDesktopPane desktopPane;
protected JComponent parent;
protected ModalityInternalFrame childFrame;
protected JComponent focusOwner;
private boolean wasCloseable;
public ModalityInternalFrame() {
init(); // here to allow netbeans to use class in gui builder
}
public ModalityInternalFrame(JComponent parent) {
this(parent, null);
}
public ModalityInternalFrame(JComponent parent, String title) {
this(parent, title, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable) {
this(parent, title, resizable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable) {
this(parent, title, resizable, closeable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
boolean maximizable) {
this(parent, title, resizable, closeable, maximizable, false);
}
public ModalityInternalFrame(JComponent parent, String title, boolean resizable, boolean closeable,
boolean maximizable,
boolean iconifiable) {
super(title, resizable, closeable, maximizable, iconifiable);
setParentFrame(parent);
//setFocusTraversalKeysEnabled(false);
if (parent != null && parent instanceof ModalityInternalFrame) {
((ModalityInternalFrame) parent).setChildFrame(ModalityInternalFrame.this);
/*
* set focus to the new frame and show the frame Code added by Jasir
*/
try {
((ModalityInternalFrame) parent).setSelected(false);
setSelected(true);
setVisible(true);
} catch (PropertyVetoException ex) {
Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
// Add glass pane
ModalityInternalGlassPane glassPane = new ModalityInternalGlassPane(this);
setGlassPane(glassPane);
// Add frame listeners
addFrameListener();
// Add frame veto listenr
addFrameVetoListener();
init();
// calculate size and position
}
private void setParentFrame(JComponent parent) {
desktopPane = JOptionPane.getDesktopPaneForComponent(parent);
this.parent = parent == null ? JOptionPane.getDesktopPaneForComponent(parent) : parent; // default to desktop if no parent given
}
public JComponent getParentFrame() {
return parent;
}
public void setChildFrame(ModalityInternalFrame childFrame) {
this.childFrame = childFrame;
}
public ModalityInternalFrame getChildFrame() {
return childFrame;
}
public boolean hasChildFrame() {
return (childFrame != null);
}
protected void addFrameVetoListener() {
addVetoableChangeListener(new VetoableChangeListener() {
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if (evt.getPropertyName().equals(JInternalFrame.IS_SELECTED_PROPERTY)
&& evt.getNewValue().equals(Boolean.TRUE)) {
if (hasChildFrame()) {
//childFrame.setSelected(true);
if (childFrame.isIcon()) {
childFrame.setIcon(false);
}
throw new PropertyVetoException("no!", evt);
}
}
}
});
}
/**
* Method to control the display of the glass pane, dependant on the frame
* being active or not
*/
protected synchronized void addFrameListener() {
addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameActivated(InternalFrameEvent e) {
if (hasChildFrame() == true) {
getGlassPane().setVisible(true);
grabFocus();
} else {
getGlassPane().setVisible(false);
}
}
@Override
public void internalFrameOpened(InternalFrameEvent e) {
getGlassPane().setVisible(false);
try {
setSelected(true);
} catch (PropertyVetoException ex) {
Logger.getLogger(ModalityInternalFrame.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void internalFrameClosing(InternalFrameEvent e) {
if (parent != null && parent instanceof ModalityInternalFrame) {
((ModalityInternalFrame) parent).childClosing();
}
}
});
}
/**
* Method to handle child frame closing and make this frame available for
* user input again with no glass pane visible
*/
protected void childClosing() {
setClosable(wasCloseable);
getGlassPane().setVisible(false);
if (focusOwner != null) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
moveToFront();
setSelected(true);
focusOwner.grabFocus();
} catch (PropertyVetoException ex) {
}
}
});
focusOwner.grabFocus();
}
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
setChildFrame(null);
getDesktopPane().setSelectedFrame(this);
System.out.println(getDesktopPane().getSelectedFrame());
}
/*
* Method to handle child opening and becoming visible.
*/
protected void childOpening() {
// record the present focused component
wasCloseable = isClosable();
setClosable(false);
focusOwner = (JComponent) getMostRecentFocusOwner();
grabFocus();
getGlassPane().setVisible(true);
getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
@Override
public void show() {
if (parent != null && parent instanceof ModalityInternalFrame) {
// Need to inform parent its about to lose its focus due
// to child opening
((ModalityInternalFrame) parent).childOpening();
}
calculateBounds();
super.show();
}
protected void init() {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 394, Short.MAX_VALUE));
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 274, Short.MAX_VALUE));
pack();
}
public void calculateBounds() {
Dimension frameSize = getPreferredSize();
Dimension parentSize = new Dimension();
Dimension rootSize = new Dimension(); // size of desktop
Point frameCoord = new Point();
if (desktopPane != null) {
rootSize = desktopPane.getSize(); // size of desktop
frameCoord = SwingUtilities.convertPoint(parent, 0, 0, desktopPane);
parentSize = parent.getSize();
}
//setBounds((rootSize.width - frameSize.width) / 2, (rootSize.height - frameSize.height) / 2, frameSize.width, frameSize.height);
// We want dialog centered relative to its parent component
int x = (parentSize.width - frameSize.width) / 2 + frameCoord.x;
int y = (parentSize.height - frameSize.height) / 2 + frameCoord.y;
// If possible, dialog should be fully visible
int ovrx = x + frameSize.width - rootSize.width;
int ovry = y + frameSize.height - rootSize.height;
x = Math.max((ovrx > 0 ? x - ovrx : x), 0);
y = Math.max((ovry > 0 ? y - ovry : y), 0);
setBounds(x, y, frameSize.width, frameSize.height);
}
/**
* Glass pane to overlay. Listens for mouse clicks and sets selected on
* associated modal frame. Also if modal frame has no children make class
* pane invisible
*/
class ModalityInternalGlassPane extends JComponent {
private ModalityInternalFrame modalFrame;
public ModalityInternalGlassPane(ModalityInternalFrame frame) {
modalFrame = frame;
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (modalFrame.isSelected() == false) {
try {
modalFrame.setSelected(true);
if (modalFrame.hasChildFrame() == false) {
setVisible(false);
}
} catch (PropertyVetoException e1) {
//e1.printStackTrace();
}
}
}
});
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(255, 255, 255, 100));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}
Я не могу заставить ваш код работать, но вот более простая версия, основанная на примере Sun, которая работает - в основном кадре есть кнопка (занимающая все доступное пространство), но нажатие кнопки заблокирован, пока внутренняя рама не будет закрыта.
Вы можете видеть, почти все, что я сделал, это заменил new JOptionPane().createInternalFrame()
бизнес с моей собственной рамой. Я предполагаю, что вы слишком усложняете вещи, когда пытаетесь выполнить собственную диспетчеризацию событий.
Или я что-то упустил?
public class Foo {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(600, 400);
frame.setLocationByPlatform(true);
JButton desktop = new JButton(new AbstractAction("Click me if you can") {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("I have been clicked");
}
});
frame.getContentPane().add(desktop);
frame.setVisible(true);
JInternalFrame modal =
new JInternalFrame("Modal Popup", false, true, false, false);
JLabel popupContent = new JLabel("I am the popup");
popupContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
modal.add(popupContent);
modal.pack();
JPanel glass = new JPanel();
glass.setOpaque(false);
glass.add(modal);
frame.setGlassPane(glass);
glass.setVisible(true);
modal.setVisible(true);
modal.addInternalFrameListener(new ModalAdapter(glass));
}
}
class ModalAdapter extends InternalFrameAdapter {
Component glass;
public ModalAdapter(Component glass) {
this.glass = glass;
// Associate dummy mouse listeners
// Otherwise mouse events pass through
MouseInputAdapter adapter = new MouseInputAdapter() {
};
glass.addMouseListener(adapter);
glass.addMouseMotionListener(adapter);
}
public void internalFrameClosed(InternalFrameEvent e) {
glass.setVisible(false);
}
}
Я просто должен был сделать это для проекта. Все, что я сделал, это передал объект главного окна Jinternalframe. Главный объект имеет семафор, который отслеживает, заблокирован модальный или нет. При закрытии Jinternalframe (расширение) вызывает семафор основного объекта. Очень просто. Это не основной код, но вы поняли:
//called frame
public CallingFrame parent;
public void setParent(CallingFrame parent_){
this.parent=parent_;
}
private void frameClosed(javax.swing.event.InternalFrameEvent evt) {
parent.modalLocked=false;
}
В моем случае приложение использует метку с частями изображения для вызова внутренних кадров, поэтому код начинается с
//calling frame
CalledFrame cf=new CalledFrame();
cf.setParent(this);
cf.setVisible(true);
modalLoacked=true;
private void jLabel1MouseReleased(java.awt.event.MouseEvent evt) {
if (modalLocked)
return;
else// (do your things)
}
Я следовал инструкциям, но большинство из них слишком усложняют ситуацию, когда один семафор сделает все, чтобы не дать вам щелкнуть мышью по какой-либо области, пока одна вызываемая рамка не закрыта.
public void internalFrameClosing(InternalFrameEvent e){ close(); }
вызов close() приведет к повторному вызову internalFrameClosing(), пока стек не переполнится.
Попробуйте полностью удалить этого слушателя.