【问题标题】:Position a Window completely on screen in multi monitor environment在多显示器环境中将窗口完全定位在屏幕上
【发布时间】:2013-10-13 17:22:37
【问题描述】:

有时我必须显示一个与现有组件相关的弹出窗口或对话框(主要示例是旁边带有日历按钮的日期输入控件)。

多年来它一直运行良好,但始终存在日历可能部分显示在屏幕外的错误(它被硬编码为显示在字段的右侧)。只是没有人注意到,因为窗口的最右边从来没有日期控件。好吧,最近添加了一个新窗口,这种情况发生了变化。

那么,我想,让我们修复一个窗口位置(在我将它定位到它应该在的位置之后)以完全显示在屏幕上。为此,我编写了一个简单的实用方法:

public static void correctWindowLocationForScreen(Window window) {
    GraphicsConfiguration gc = window.getGraphicsConfiguration();
    Rectangle screenRect = gc.getBounds();
    Rectangle windowRect = window.getBounds();
    Rectangle newRect = new Rectangle(windowRect);
    if (windowRect.x + windowRect.width > screenRect.x + screenRect.width)
        newRect.x = screenRect.x + screenRect.width - windowRect.width;
    if (windowRect.y + windowRect.height > screenRect.y + screenRect.height)
        newRect.y = screenRect.y + screenRect.height - windowRect.height;
    if (windowRect.x < screenRect.x)
        newRect.x = screenRect.x; 
    if (windowRect.y < screenRect.y)
        newRect.y = screenRect.y;
    if (!newRect.equals(windowRect))
        window.setLocation(newRect.x, newRect.y);
}

问题解决了。或不。我使用触发组件(使日历出现的按钮)的屏幕坐标定位我的窗口:

JComponent invoker = ... // passed in from the date field (a JButton)
Window owner = SwingUtilities.getWindowAncestor(invoker);
JDialog dialog = new JDialog(owner);
dialog.setLocation(invoker.getLocationOnScreen());
correctWindowLocationForScreen(dialog);

如果“调用者”组件位于一个跨越两个屏幕的窗口中,就会发生破坏。显然“window.getGraphicsConfiguration()”返回窗口左上角恰好位于的任何图形配置。那不一定是窗口内日期组件所在的屏幕。

那么在这种情况下如何正确定位我的对话框?

【问题讨论】:

    标签: java swing


    【解决方案1】:

    可以遍历所有设备,并找到该点所在的监视器。然后保持该矩形。

    GraphicsEnvironment.getScreenDevices

    这不会使用当前的窗口,但你已经发现一个窗口可能会显示在多个监视器中。

    有用的可能是Component.getLocationOnScreen

    【讨论】:

    • 我可能应该更好地定义我所说的正确定位。检查所有可用的屏幕矩形是解决问题的方法,但我发现它比“保持那个矩形”要棘手得多(主要是因为屏幕可以有不同的分辨率)。我根据你的建议做了一些事情,但它既不漂亮也不短——而且可能仍然充满漏洞。
    【解决方案2】:

    好的,这就是我最终得到的结果(处理奇怪边缘情况的代码墙)。

    correctWindowLocationForScreen() 将重新定位一个窗口,如果它完全在可见屏幕区域内(最简单的情况,它完全在一个屏幕上。硬情况,它跨越多个屏幕)。如果窗口仅离开整个屏幕区域一个像素,则使用找到的第一个屏幕矩形重新定位。如果窗口不适合屏幕,它位于左上角并延伸到屏幕右下角(它由 positionInsideRectangle() 检查/更改坐标的顺序暗示​​)。

    考虑到需求很简单,这很复杂。

    /**
     * Check that window is completely on screen, if not correct position.
     * Will not ensure the window fits completely onto the screen.
     */
    public static void correctWindowLocationForScreen(final Window window) {
        correctComponentLocation(window, getScreenRectangles());
    }
    
    /**
     * Set the component location so that it is completely inside the available
     * regions (if possible).
     * Although the method will make some effort to place the component
     * nicely, it may end up partially outside the regions (either because it
     * doesn't fit at all, or the regions are placed badly).
     */
    public static void correctComponentLocation(final Component component, final Rectangle ... availableRegions) {
        // check the simple cases (component completely inside one region, no regions available)
        final Rectangle bounds = component.getBounds();
        if (availableRegions == null || availableRegions.length <= 0)
            return;
        final List<Rectangle> intersecting = new ArrayList<>(3);
        for (final Rectangle region : availableRegions) {
            if (region.contains(bounds)) {
                return;
            } else if (region.intersects(bounds)) {
                // partial overlap
                intersecting.add(region);
            }
        }
        switch (intersecting.size()) {
            case 0:
                // position component in the first available region
                positionInsideRectangle(component, availableRegions[0]);
                return;
            case 1:
                // position component in the only intersecting region
                positionInsideRectangle(component, intersecting.get(0));
                return;
            default:
                // uuuh oooh...
                break;
        }
        // build area containing all detected intersections
        // and check if the bounds fall completely into the intersection area
        final Area area = new Area();
        for (final Rectangle region : intersecting) {
            final Rectangle2D r2d = new Rectangle2D.Double(region.x, region.y, region.width, region.height);
            area.add(new Area(r2d));
        }
        final Rectangle2D boundsRect = new Rectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height);
        if (area.contains(boundsRect))
            return;
        // bah, just place it in the first intersecting region...
        positionInsideRectangle(component, intersecting.get(0));
    }   
    
    /**
     * Position component so that its completely inside the rectangle.
     * If the component is larger than the rectangle, component will
     * exceed to rectangle bounds to the right and bottom, e.g.
     * the component is placed at the rectangles x respectively y.
     */
    public static void positionInsideRectangle(final Component component, final Rectangle region) {
        final Rectangle bounds = component.getBounds();
        int x = bounds.x;
        int y = bounds.y;
        if (x + bounds.width > region.x + region.width)
            x = region.x + region.width - bounds.width;
        if (y + bounds.height > region.y + region.height)
            y = region.y + region.height - bounds.height;
        if (region.x < region.x)
            x = region.x; 
        if (y < region.y)
            y = region.y;
        if (x != bounds.x || y != bounds.y)
            component.setLocation(x, y);
    }
    
    /**
     * Gets the available display space as an arrays of rectangles
     * (there is one rectangle for each screen, if the environment is
     * headless the resulting array will be empty).
     */
    public static Rectangle[] getScreenRectangles() {
        try {
            Rectangle[] result;
            final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            final GraphicsDevice[] devices = ge.getScreenDevices();
            result = new Rectangle[devices.length];
            for (int i=0; i<devices.length; ++i) {
                final GraphicsDevice gd = devices[i];
                result[i] = gd.getDefaultConfiguration().getBounds();
            }
            return result;
        } catch (final Exception e) {
            return new Rectangle[0];
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-24
      • 2014-06-07
      • 2011-06-03
      相关资源
      最近更新 更多