当我在 JDesktopPane 中有 JInternalFrame 时,JInternalFrame 是可移动的(这很好)。但是,可以将其移到 JDesktopPane 的可见范围之外(我不太喜欢)
要亲自查看,这里有一些示例代码:
public static void main(String[] args) {
JFrame frame = new JFrame("JDesktopPane");
JDesktopPane tableDisplay = new JDesktopPane();
JInternalFrame internalFrame = new JInternalFrame("JInternalFrame",true,true,true,true);
internalFrame.setContentPane(new JLabel("Content"));
internalFrame.pack();
internalFrame.setVisible(true);
tableDisplay.add(internalFrame, JDesktopPane.POPUP_LAYER);
frame.setContentPane(tableDisplay);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(400, 300));
frame.setVisible(true);
}
是否可以设置 JInternalFrame 或 JDesktopPane 以使它们不允许这样做?
负责移动/调整大小的协作者是DesktopPaneManager。所以我会尝试将移动限制在窗格内。这是一个快速而肮脏的概念证明:
JDesktopPane background = new JDesktopPane();
JInternalFrame internalFrame = new JInternalFrame("Internal Frame",
true, true, true, true);
DesktopManager manager = new DefaultDesktopManager() {
/** This moves the <code>JComponent</code> and repaints the damaged areas. */
@Override
public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) return;
f.setBounds(newX, newY, newWidth, newHeight);
if(didResize) {
f.validate();
}
}
protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
if (newX < 0 || newY < 0) return false;
if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
return true;
}
};
background.setDesktopManager(manager);
显然有一些问题需要解决:-)
编辑
只是为了澄清:“无响应”我的意思是用户必须再次释放并按下/拖动(一旦内部框架到达桌面边界)才能进一步移动。这并不奇怪,因为 BorderListener(即 BasicInternalFrame 安装的 mouseListener)保留与初始按下相关的一些状态,然后请求相对于该初始位置重新定位。在框架卡在某处的情况下拖动鼠标会混淆内部状态。
有趣的是,看代码,似乎有意图限制移动不让它被推到外面,
// Make sure we stay in-bounds
if(newX + i.left <= -__x)
newX = -__x - i.left + 1;
if(newY + i.top <= -__y)
newY = -__y - i.top + 1;
if(newX + __x + i.right >= pWidth)
newX = pWidth - __x - i.right - 1;
if(newY + __y + i.bottom >= pHeight)
newY = pHeight - __y - i.bottom - 1;
不过,这是相对于当前鼠标位置的。
完全归功于克利奥帕特拉为我指明了正确的方向。
我想我已经解决了无响应问题,并在这里分享我的解决方案。根据 kleopatra 的回答,如果鼠标点位于窗格之外,则内部框架位于窗格的边缘。此外,它继续跟随鼠标 - 即,如果您将鼠标从窗格底部移开,然后绕到窗格的右侧,框架将沿着窗格底部,然后向右上方移动窗格中的沙子。
public class BoundedDesktopManager extends DefaultDesktopManager {
@Override
public void beginDraggingFrame(JComponent f) {
// Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
}
@Override
public void beginResizingFrame(JComponent f, int direction) {
// Don't do anything. Needed to prevent the DefaultDesktopManager setting the dragMode
}
@Override
public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
if (!inBounds((JInternalFrame) f, newX, newY, newWidth, newHeight)) {
Container parent = f.getParent();
Dimension parentSize = parent.getSize();
int boundedX = (int) Math.min(Math.max(0, newX), parentSize.getWidth() - newWidth);
int boundedY = (int) Math.min(Math.max(0, newY), parentSize.getHeight() - newHeight);
f.setBounds(boundedX, boundedY, newWidth, newHeight);
} else {
f.setBounds(newX, newY, newWidth, newHeight);
}
if(didResize) {
f.validate();
}
}
protected boolean inBounds(JInternalFrame f, int newX, int newY, int newWidth, int newHeight) {
if (newX < 0 || newY < 0) return false;
if (newX + newWidth > f.getDesktopPane().getWidth()) return false;
if (newY + newHeight > f.getDesktopPane().getHeight()) return false;
return true;
}
}
尝试设置JFrame的
FlowLayout
并使用add()
方法添加JDesktopPane。
frame.setLayout(new FlowLayout());
tableDisplay.setPreferredSize(new Dimension(100,100));
frame.add(tableDisplay);
JInternalFrame 的顶部不要被隐藏,这一点很重要,因为那是其抓取栏和控制按钮所在的位置。因此,作为设置边界之前的最后一步,请确保 y 值不为负:例如,使用 y = Math.max(0, y) 之类的语句。
这完全满足了我的需求。它将拖动的框架保留在可见区域中,并且当鼠标光标移到桌面窗格区域的边缘时,它会“滑动”窗口。
JDesktopPane desktop = new JDesktopPane();
DesktopManager manager = new DefaultDesktopManager() {
@Override
public void dragFrame(JComponent f, int newX, int newY) {
JDesktopPane desktop = ((JInternalFrame) f).getDesktopPane();
newX = Math.clamp(newX, 0, desktop.getWidth() - f.getWidth());
newY = Math.clamp(newY, 0, desktop.getHeight() - f.getHeight());
super.dragFrame(f, newX, newY);
}
};
background.setDesktopManager(desktop);
Math.clamp
在 Java 21 中可用。如果需要,请自行实现。