问题
My Swing application has to show a modal dialog to the user. Sorry for not posting SSCCE.
topContainer
might be JFrame
or JApplet
.
private class NewGameDialog extends JDialog {
public NewGameDialog () {
super(SwingUtilities.windowForComponent(topContainer), "NEW GAME", ModalityType.APPLICATION_MODAL);
//add components here
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
//TODO:
setSize(new Dimension(250, 200));
setLocation(650, 300);
}
}
I start the dialog like this on network event
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
NewGameDialog dialog = new NewGameDialog();
dialog.setVisible(true);
}
});
The problem is to set optimal location for my dialog.
1) If it is set as absolute value, and I move the app frame to the second screen, then dialog is shown on the first screen which is weird.
2) If it is set a relative value to JFrame, it might appear that user moved the app frame outside of the screen and the dialog being relatively located would not be visible to the user. And because it is modal, the game would be stuck.
What is the best solution considering two above mentioned issues?
回答1:
This reminded me of a very favourite post of mine, using Window.setLocationByPlatform(true), on StackOverflow.
How to best position Swing GUIs
EDIT 1 :
You can add a FocusListener
to your JDialog
and on focusGained(...)
method, you can use setLocationRelativeTo(null)
for both the JFrame
and the JDialog
, so that they both come to the center of the screen no matter where they are before.
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/**
* Created with IntelliJ IDEA.
* User: Gagandeep Bali
* Date: 1/14/13
* Time: 7:34 PM
* To change this template use File | Settings | File Templates.
*/
public class FrameFocus
{
private JFrame mainwindow;
private CustomDialog customDialog;
private void displayGUI()
{
mainwindow = new JFrame("Frame Focus Window Example");
customDialog = new CustomDialog(mainwindow, "Modal Dialog", true);
mainwindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
JButton mainButton = new JButton(
"Click me to open a MODAL Dialog");
mainButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!customDialog.isShowing())
customDialog.setVisible(true);
}
});
contentPane.add(mainButton);
mainwindow.setContentPane(contentPane);
mainwindow.pack();
mainwindow.setLocationByPlatform(true);
mainwindow.setVisible(true);
}
public static void main(String... args)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
new FrameFocus().displayGUI();
}
});
}
}
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
mainWindow.setLocationRelativeTo(null);
setLocationRelativeTo(null);
}
@Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
EDIT 2 :
I searched a bit here and there, and it turns out, in my opinion, that actually on which Monitor Screen
your application comes at the first instance, will determine it's GraphicsConfiguration. Though as I roamed through the API, there is only a getter method for the said GraphicsConfiguration
thingy and no setter methods for the same (Still You can specify one through the constructor of any top level Window i.e. JFrame(...)/JDialog(...)).
Now you can occupy your head with this code, which can be used to determine the appropriate location, that you want to set, again, you might have to use focusGain()
method in my opinion, to satisfy condition 2 of your question. Have a look at the code attached, though no need to create a new JFrame/JDialog
, just watch how to get coordinates for the screen (that you can add in the focusGain()
method to determine the location of the whole Application.)
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc =
gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
JFrame f = new
JFrame(gs[j].getDefaultConfiguration());
Canvas c = new Canvas(gc[i]);
Rectangle gcBounds = gc[i].getBounds();
int xoffs = gcBounds.x;
int yoffs = gcBounds.y;
f.getContentPane().add(c);
f.setLocation((i*50)+xoffs, (i*60)+yoffs);
f.show();
}
}
EDIT 3 :
Try to change this :
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
to just :
setLocationRelativeTo(mainWindow);
To test the above thingy, I used my FrameFocus
Class as is, though I had added your changes to my CustomDialog
method, as shown in this modified CustomDialog
Class.
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
//mainWindow.setLocationRelativeTo(null);
//setLocationRelativeTo(null);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
//x = (int) (loc.getX() + (mainWindow.getWidth() - CustomDialog.this.getWidth()) / 2);
//y = (int) (loc.getY() + (mainWindow.getHeight() - CustomDialog.this.getHeight()) / 2);
//CustomDialog.this.setLocation(x, y);
CustomDialog.this.setLocationRelativeTo(mainWindow);
break;
}
}
}
}
@Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
回答2:
I think, best would be to center the dialog in the middle of the current screen as described here.
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
int x = (screenSize.width - d.getWidth()) / 2;
int y = (screenSize.height - d.getHeight()) / 2;
d.setLocation(x, y);
This always works and how it can be invisible to the user if it is right in the center of the screen? And setLocationRelativeTo
can also be used but you need to invoke it at the right time.
回答3:
use JDialog.setLocation() for moving JDialog
on desired Point on the screen
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
public class JDialogAtPoint {
private JFrame frame = new JFrame();
private JPanel panel = new JPanel();
private JDialog dialog;
private Point location;
public JDialogAtPoint() {
createGrid();
createDialog();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
private void createGrid() {
panel.setLayout(new GridLayout(3, 3, 4, 4));
int l = 0;
int row = 3;
int col = 3;
JButton buttons[][] = new JButton[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
buttons[i][j] = new JButton("");
buttons[i][j].putClientProperty("column", i + 1);
buttons[i][j].putClientProperty("row", j + 1);
buttons[i][j].setAction(updateCol());
panel.add(buttons[i][j]);
l++;
}
}
}
private void createDialog() {
dialog = new JDialog();
dialog.setAlwaysOnTop(true);
dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
JPanel pane = (JPanel) dialog.getContentPane();
pane.setBorder(new EmptyBorder(20, 20, 20, 20));
dialog.pack();
}
public Action updateCol() {
return new AbstractAction("Display JDialog at Point") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
System.out.println("Locations coordinates" + btn.getLocation());
System.out.println("clicked column "
+ btn.getClientProperty("column")
+ ", row " + btn.getClientProperty("row"));
if (!dialog.isVisible()) {
showingDialog(btn.getLocationOnScreen());
}
}
};
}
private void showingDialog(final Point loc) {
dialog.setVisible(false);
location = loc;
int x = location.x;
int y = location.y;
dialog.setLocation(x, y);
Runnable doRun = new Runnable() {
@Override
public void run() {//dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
};
SwingUtilities.invokeLater(doRun);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JDialogAtPoint cf = new JDialogAtPoint();
}
});
}
}
回答4:
With the help of all 3 answerers I have come up with code which seems exactly what I need. First, JFrame
got placed in the middle of current screen and then JDialog
accordingly.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
break;
}
}
}
来源:https://stackoverflow.com/questions/14273836/optimal-location-for-a-modal-jdialog-to-avoid-stuck