У меня есть подкласс JSplitPane
, который предназначен для того, чтобы разрешить фиксированное расположение делителя с правого или нижнего края вместо левого или верхнего.
С этой целью яиспользование ComponentChangeListener
для определения размера компонента и пересчета местоположения разделителя относительно ширины или высоты.
Все это работает безупречно. Но теперь я добавляю PropertyChangeListener
, чтобы зафиксировать настройку разделенного местоположения пользователем и сохранить это левое / верхнее относительное значение в качестве смещения от правой / нижней границы для последующего использования при изменении размера.
Но у меня есть своего рода каскадная проблема:
- Вы изменяете размер компонента
- A
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). Это тот PCE, следующий за CE, который мне нужно подавить.
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();
}
}
Обновление: Мне удалось решить эту проблему, подключив MouseListener к разделителю через пользовательский интерфейс(((BasicSplitPaneUI)getUI()).getDivider.addMouseListener(...)
) и сохраните данные о местоположении в событии mouseReleased()
. Это работает, но было бы хорошо знать, есть ли способ подавления PropertyChangeEvents ...