0

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

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

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

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);

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

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

4

2 回答 2

1

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

请参阅GraphicsEnvironment.getScreenDevices

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

有用的可能是Component.getLocationOnScreen

于 2013-10-13T18:13:55.013 回答
0

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

如果窗口完全在可见屏幕区域内,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];
    }
}
于 2013-10-14T13:19:18.307 回答