问题
What's the best way to 'link' JSliders to put a cap on the combined value?
Imagine a fighting game where the user can specify character traits like speed, agility, endurance and accuracy. The user is given 100 points to allocate however he chooses, but the total value of all the sliders must not exceed 100. That is, the sliders should not allow such increases.
If the user has allocated 30 points each to speed, agility and endurance, then the slider for accuracy should allow a maximum of 10 points (because 30 + 30 + 30 + 10 = 100). Notwithstanding, the range of the each slider should remain unchanged (in this case, 0 to 100).
I hope this makes sense. Thanks for the help.
回答1:
"nichts einfacher als das" - dachte ... (insert name :-)
Basically, all such enhanced functionality must be implemented in the model: for JSlider, that's a BoundedRangeModel. Implement/extend and enforce its value to respect the "combined" max. Something like
public static class LimitedBoundedRangeModel extends DefaultBoundedRangeModel {
BoundedRangeModel limit;
public LimitedBoundedRangeModel(BoundedRangeModel limit) {
this.limit = limit;
}
/**
* @inherited <p>
*/
@Override
public void setRangeProperties(int newValue, int newExtent, int newMin,
int newMax, boolean adjusting) {
if (limit != null) {
int combined = newValue + limit.getValue();
if (combined > newMax) {
newValue = newMax - limit.getValue();
}
}
super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
}
}
// use
LimitedBoundedRangeModel firstModel = new LimitedBoundedRangeModel(null);
LimitedBoundedRangeModel secondModel = new LimitedBoundedRangeModel(firstModel);
firstModel.limit= secondModel;
JSlider first = new JSlider(firstModel);
JSlider second = new JSlider(secondModel);
While simple (two dependents only) and crude (direct bidirectional coupling) and as such not really usable out in the wild, it should at least work ... but doesn't - one of those surprises which hit me from time to time ;-) The visual problem is the thumb-position:
- the combined max is respected when clicking to the right of the thumb: the thumb never moves over the threshhold
- when dragging, the thumb can be moved everywhere which might be expected, as the ui isn't aware of the model tweak - it knows only the "local" max
- on drag-end, the thumb remains at that invalid position.. which smells like a bug as now the thumb is out-off synch with the model
The reason for this misbehaviour is that the changeListener in Handler: it doesn't recalculate its thumb position when dragging (which is okay). The subtle bug is, that the internal dragging flag is only reset after the model's adjusting property is reset, so missing the very last notification about the final value ...
Hack around is to invoke the firing of an additional changeEvent, if the adjusting flag changes from true to false
boolean invoke =
(adjusting != getValueIsAdjusting()) && !adjusting;
super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
if (invoke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
fireStateChanged();
}
});
}
回答2:
Create 4 sliders and some variables: the maximum total value (say, 100) and the current total (0).
When the value of any of the sliders modifies, do:
// I hope my logic isn't failing me at this point
if (slider.getValue() > (maxTotal - currentTotal))
slider.setValue(maxTotal - currentTotal);
currentTotal = slider1.getValue() + slider2.getValue()
+ slider3.getValue() + slider4.getValue();
来源:https://stackoverflow.com/questions/7468314/linked-jsliders-with-maximum-combined-value