Optimal location for a modal JDialog to avoid stuck

白昼怎懂夜的黑 提交于 2020-02-14 17:09:13

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!