JSpinnerの数値の合計がグループ内で一定になるよう設定する
Total: 375
, Today: 1
, Yesterday: 1
Posted by aterai at
Last-modified:
概要
複数のSpinnerNumberModel
をセットしたグループを作成し、グループ内でその数値の合計が一定になるよう設定します。
Screenshot
Advertisement
サンプルコード
class SpinnerNumberModelGroup {
private final int expectedSum;
private final Deque<SpinnerNumberModel> candidates = new ArrayDeque<>();
private final ChangeListener changeListener = e -> {
SpinnerNumberModel source = (SpinnerNumberModel) e.getSource();
update(source);
};
private boolean updating;
protected SpinnerNumberModelGroup(int expectedSum) {
this.expectedSum = expectedSum;
}
@SuppressWarnings("PMD.UnusedAssignment")
private void update(SpinnerNumberModel source) {
if (updating) {
return;
}
updating = true;
if (candidates.size() - 1 > 0) {
int delta = computeSum() - expectedSum;
if (delta > 0) {
distributeRemove(delta, source);
} else {
distributeAdd(delta, source);
}
}
updating = false;
}
private void distributeRemove(int delta, SpinnerNumberModel source) {
int counter = 0;
int remaining = delta;
while (remaining > 0) {
SpinnerNumberModel model = candidates.removeFirst();
counter++;
if (Objects.equals(model, source)) {
candidates.addLast(model);
} else {
Object prev = model.getPreviousValue();
if (prev instanceof Integer) {
model.setValue(prev);
remaining--;
counter = 0;
}
candidates.addLast(model);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String msg = "Can not distribute " + delta + " among " + candidates;
throw new IllegalArgumentException(msg);
}
}
}
private void distributeAdd(int delta, SpinnerNumberModel source) {
int counter = 0;
int remaining = -delta;
while (remaining > 0) {
SpinnerNumberModel model = candidates.removeLast();
counter++;
if (Objects.equals(model, source)) {
candidates.addFirst(model);
} else {
// if (model.getNumber().intValue() < (int) model.getMaximum()) {
Object next = model.getNextValue();
if (next instanceof Integer) {
model.setValue(next);
remaining--;
counter = 0;
}
candidates.addFirst(model);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String msg = "Can not distribute " + delta + " among " + candidates;
throw new IllegalArgumentException(msg);
}
}
}
private int computeSum() {
return candidates.stream()
.map(SpinnerNumberModel::getNumber)
.mapToInt(Number::intValue)
.sum();
}
public void add(SpinnerNumberModel spinner) {
candidates.add(spinner);
spinner.addChangeListener(changeListener);
}
}
View in GitHub: Java, Kotlin解説
Deque<SpinnerNumberModel>
でSpinnerNumberModel
のグループを作成- java - Multiple JSliders reacting to each other to always equal 100 percent - Stack Overflowの
JSlider
のサンプルを参考にSpinnerNumberModel
を使用するよう変更
- java - Multiple JSliders reacting to each other to always equal 100 percent - Stack Overflowの
- グループ内の
SpinnerNumberModel
にChangeListener
を追加し、値の更新イベントを取得 - ひとつの
SpinnerNumberModel
が更新されたらその差分を計算 Deque<SpinnerNumberModel>
の末尾からSpinnerNumberModel
を取り出して差分を配分し、Deque<SpinnerNumberModel>
の先頭に戻す- 取り出した
SpinnerNumberModel
が更新イベント元のSpinnerNumberModel
の場合はなにもせずに先頭に戻す - 差分が
0
になるまでこの処理を繰り返す
- 取り出した
参考リンク
- java - Multiple JSliders reacting to each other to always equal 100 percent - Stack Overflow
- java - Multiple JSliders activating and deactivating - Sharing values - Stack Overflow