java - 当分隔符移动到 ComponentListener 内时,JSplitPane 会阻止 PropertyChangeEvent

标签 java swing events

我有一个 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 将 MouseListener 附加到分隔符来解决该问题 (((BasicSplitPaneUI)getUI()).getDivider.addMouseListener(... )) 并将位置数据保存在 mouseReleased() 事件中。它有效,但知道是否有办法抑制 PropertyChangeEvents 仍然是件好事...

最佳答案

but it'd still be good to know if there is a way of suppressing 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));
        }
    }

关于java - 当分隔符移动到 ComponentListener 内时,JSplitPane 会阻止 PropertyChangeEvent,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58666224/

相关文章:

swing - 如何制作自己的 Scala UI 事件

jquery定义自定义事件

java - 如何实现action bar的效果消失/出现? (谷歌现在效果)

java - 自动监控网页变化

java - 如何在 spring-boot 1.4.3 中覆盖 spring-boot 应用程序属性

java - 如何检查用户是否已从 Google/Facebook/GitHub 等注销 - OAuth2

Java:在 JFrame 上添加背景图像

java - 在 JTable 中添加 JSpinner 时出现非法参数异常

java - JGroups 可以用来形成 COMET/Long-Polling 的 Java 等价物吗?

jQuery 文档说 .submit() 事件在 IE 中不会冒泡。这是什么意思?