当分隔符在ComponentListener内部移动时,JSplitPane阻止PropertyChangeEvent



我有一个JSplitPane的子类,它旨在允许从右边缘或底边而不是从左边缘或顶部固定分隔符的位置。

为此,我使用ComponentChangeListener来捕捉何时调整组件的大小,并重新计算分隔器相对于宽度或高度的位置。

这一切都完美无瑕。但现在我添加了一个PropertyChangeListener来捕捉用户对分割位置的调整,并将该左/上相对值存储为与右/下边界的偏移量,以便稍后在调整大小时使用。

但我有一种级联问题:

  • 调整组件大小
  • 触发ComponentChangeEvent
  • 分隔器移动到正确的位置
  • 导致PropertyChangeEvent触发
  • 然后使用不正确的数据重新计算移动的分隔符的位置

所以除法器最终会弹得到处都是。

我试过手动放入一个"禁止"标志(只是一个简单的boolean(,有时可以阻止事件,但通常不会,所以这不是解决问题的方法

有线索吗?

我的propertyChange方法看起来像:

public void propertyChange(PropertyChangeEvent e) {
if (inhibit) return;
if (e.getPropertyName().equals("dividerLocation")) {
int pos = (Integer)e.getNewValue();
if (right == -1) {
left = pos;
} else {
Dimension d = getSize();
if (orient == JSplitPane.VERTICAL_SPLIT) {
right = d.height - pos;
} else {
right = d.width - pos;
}
}
}
}

componentResized是:

public void componentResized(ComponentEvent e) {
if (!inhibit) {
updateDividerLocation();
}
}
void updateDividerLocation() {
inhibit = true;
if (right == -1) { // Left / top is fixed
setDividerLocation(left);
} else {
Dimension d = getSize();
if (orient == JSplitPane.VERTICAL_SPLIT) {
setDividerLocation(d.height - right);
} else {
setDividerLocation(d.width - right);
}
}
inhibit = false;
}

正如你所看到的,我在那里有inhibit标志,正如我所说,这不正确。

那么,防止setDividerLocation函数触发PropertyChangeEvent的正确方法是什么?我是否必须在调整组件大小的同时完全删除PropertyChangeListener,然后再添加它?这会有帮助吗?

注意:当显示更新可能有一点滞后时,这一点最为明显。这里有一个SSCCE(尽管我担心它不是那么"小"(可以证明这个问题。这个问题的结果在这个SSCCE中并不总是很明显,因为窗口中没有太多内容(我发现向右边缘向左急剧拖动是一个很好的方法,LaF有助于更慢、更图形化(,但在整个程序中这一点非常明显。然而,可以从调整窗口大小导致"CE"输出(ComponentEvent(和立即产生"PCE"输出(PropertyChangeEvent(的事实中看出效果。我需要抑制的是CE之后的PCE。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
public class TestClass {
class AbsoluteSplitPane extends JSplitPane implements ComponentListener, PropertyChangeListener {
int left = -1; // and top
int right = -1; // and bottom
int orient;
public AbsoluteSplitPane(int orientation, Component a, Component b) {
super(orientation, a, b);
orient = orientation;
addComponentListener(this);
addPropertyChangeListener(this);
}
void updateDividerLocation() {
if (right == -1) { // Left / top is fixed
setDividerLocation(left);
} else {
Dimension d = getSize();
if (orient == JSplitPane.VERTICAL_SPLIT) {
setDividerLocation(d.height - right);
} else {
setDividerLocation(d.width - right);
}
}
}
public void setLeftSize(int s) {
left = s;
right = -1;
updateDividerLocation();
}
public void setRightSize(int s) {
left = -1;
right = s;
updateDividerLocation();
}
public void setTopSize(int s) {
left = s;
right = -1;
updateDividerLocation();
}
public void setBottomSize(int s) {
left = -1;
right = s;
updateDividerLocation();
}
public void componentHidden(ComponentEvent e) {
}
public void componentShown(ComponentEvent e) {
}
public void componentMoved(ComponentEvent e) {
}
public void componentResized(ComponentEvent e) {
updateDividerLocation();
System.err.println(String.format(" CE: Left: %d Right: %d", left, right));
}
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals("dividerLocation")) {
int pos = (Integer)e.getNewValue();
if (right == -1) {
left = pos;
} else {
Dimension d = getSize();
if (orient == JSplitPane.VERTICAL_SPLIT) {
right = d.height - pos;
} else {
right = d.width - pos;
}
}
System.err.println(String.format("PCE: Left: %d Right: %d", left, right));
}
}
}
public TestClass() {
JFrame frame = new JFrame("Test Window");
JPanel left = new JPanel();
JPanel mid = new JPanel();
JPanel right = new JPanel();
AbsoluteSplitPane split1 = new AbsoluteSplitPane(JSplitPane.HORIZONTAL_SPLIT, mid, right);
AbsoluteSplitPane split2 = new AbsoluteSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, split1);
split1.setRightSize(200);
split2.setLeftSize(200);
frame.add(split2);
frame.setSize(400, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
new TestClass();
}
}

更新:我已经设法解决了这个问题,通过UI(((BasicSplitPaneUI)getUI()).getDivider.addMouseListener(...)(将鼠标定位器连接到分隔器上,并将位置数据保存在mouseReleased()事件上。它是有效的,但最好知道是否有抑制PropertyChangeEvents的方法。。。

但最好知道是否有抑制PropertyChangeEvents的方法。。。

public void componentResized(ComponentEvent e) {
removePropertyChangeListener( this );
updateDividerLocation();
addPropertyChangeListener( this );
System.err.println(String.format(" CE: Left: %d Right: %d", left, right));
}

编辑:

也许您可以查看EDT上导致生成PropertyChangeEvent的事件?

public void propertyChange(PropertyChangeEvent e)
{
if (e.getPropertyName().equals("dividerLocation"))
{
AWTEvent event = EventQueue.getCurrentEvent();
if (event != null
&& (event.getID() != MouseEvent.MOUSE_RELEASED))
return;
int pos = (Integer)e.getNewValue();
if (right == -1) {
left = pos;
} else {
Dimension d = getSize();
if (orient == JSplitPane.VERTICAL_SPLIT) {
right = d.height - pos;
} else {
right = d.width - pos;
}
}
System.err.println(String.format("PCE: Left: %d Right: %d", left, right));
}
}

最新更新