上下文
我正在编写一段包含1个以上Sliders的代码。这些滑块组成一个组。该组滑块值的总和必须始终为100。但是,作为一项必需功能,用户可以禁用和启用(添加/删除)该组滑块。因此,这需要正确调整滑块值。
尝试编写自己的小组代码后,我决定寻找一种更好的/经过测试和实施的代码。它比我的有所改善,但是出现了一些问题。
主要问题
通过选中或取消选中复选框来添加或删除滑块会导致错误,并且滑块会停止工作。请注意,在这种情况下,添加仅意味着启用先前禁用的滑块(通过取消选中复选框)。
初步解决方案
以下代码在stackoverflow中找到。我确实在代码中实现了,但是由于无法发布,因此我决定调整在stackoverflow示例中找到的代码来表示我的情况。
如何?
任何帮助将不胜感激。我不确定如何解决此问题而不引起更多错误。我确实尝试过重新调整在更新方法上完成的演算,但这只会造成更多的废话。我在这里找不到所有尝试的成果,因为stackoverflow会说太多代码,而且我不确定它是否有助于找到答案。
参考
https://stackoverflow.com/a/21391448/2280645
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ConnectedSliders {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JSlider s0 = new JSlider(0, 100, 30);
JSlider s1 = new JSlider(0, 100, 40);
JSlider s2 = new JSlider(0, 100, 30);
SliderGroup sliderGroup = new SliderGroup();
//sliderGroup.add(s0);
//sliderGroup.add(s1);
//sliderGroup.add(s2);
JPanel panel = new JPanel(new GridLayout(0, 3));
panel.add(s0);
panel.add(createListeningLabel(s0));
panel.add(createCheckBox(s0, sliderGroup));
panel.add(s1);
panel.add(createListeningLabel(s1));
panel.add(createCheckBox(s1, sliderGroup));
panel.add(s2);
panel.add(createListeningLabel(s2));
panel.add(createCheckBox(s2, sliderGroup));
panel.add(createListeningLabel(s0, s1, s2));
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JLabel createListeningLabel(final JSlider... sliders) {
final JLabel label = new JLabel("");
for (JSlider slider : sliders) {
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int sum = 0;
for (JSlider slider : sliders) {
sum += slider.getValue();
}
label.setText("Sum: " + sum);
}
});
}
return label;
}
private static JLabel createListeningLabel(final JSlider slider) {
final JLabel label = new JLabel("");
slider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
label.setText(String.valueOf(slider.getValue()));
}
});
return label;
}
private static JCheckBox createCheckBox(final JSlider slider, SliderGroup group) {
final JCheckBox checkBox = new JCheckBox();
checkBox.setSelected(true);
checkBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
group.add(slider);
slider.setEnabled(true);
} else if(e.getStateChange() == ItemEvent.DESELECTED) {
group.remove(slider);
slider.setEnabled(false);
}
}
});
return checkBox;
}
}
class SliderGroup {
private final Map<JSlider, Integer> values;
private final LinkedList<JSlider> candidates;
private final ChangeListener changeListener;
private boolean updating = false;
SliderGroup() {
this.values = new HashMap<JSlider, Integer>();
this.candidates = new LinkedList<JSlider>();
changeListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
update(source);
}
};
}
private void update(JSlider source) {
if (updating) {
return;
}
updating = true;
int delta = source.getValue() - values.get(source);
if (delta > 0) {
distributeRemove(delta, source);
} else {
distributeAdd(delta, source);
}
for (JSlider slider : candidates) {
values.put(slider, slider.getValue());
}
updating = false;
}
private void distributeRemove(int delta, JSlider source) {
int counter = 0;
int remaining = delta;
while (remaining > 0) {
JSlider slider = candidates.removeFirst();
counter++;
if (slider == source) {
candidates.addLast(slider);
} else {
if (slider.getValue() > 0) {
slider.setValue(slider.getValue() - 1);
remaining--;
counter = 0;
}
candidates.addLast(slider);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String message = "Can not distribute " + delta + " among " + candidates;
//System.out.println(message);
//return;
throw new IllegalArgumentException(message);
}
}
}
private void distributeAdd(int delta, JSlider source) {
int counter = 0;
int remaining = -delta;
while (remaining > 0) {
JSlider slider = candidates.removeLast();
counter++;
if (slider == source) {
candidates.addFirst(slider);
} else {
if (slider.getValue() < slider.getMaximum()) {
slider.setValue(slider.getValue() + 1);
remaining--;
counter = 0;
}
candidates.addFirst(slider);
if (remaining == 0) {
break;
}
}
if (counter > candidates.size()) {
String message = "Can not distribute " + delta + " among " + candidates;
//System.out.println(message);
//return;
throw new IllegalArgumentException(message);
}
}
}
void add(JSlider slider) {
candidates.add(slider);
values.put(slider, slider.getValue());
slider.addChangeListener(changeListener);
}
void remove(JSlider slider) {
candidates.remove(slider);
values.remove(slider);
slider.removeChangeListener(changeListener);
}
}
答案 0 :(得分:2)
对您链接的示例进行较小的调整,这应该是可行的。
基本思想是不仅要具有最初用于构造组的SliderGroup#add
和remove
方法,而且还要具有addAndAdjust
和removeAndAdjust
除了添加/删除滑块之外),使用仅在更改一个滑块的值时最初调整滑块的方法,才能分配添加或删除的滑块的值。
我还为复选框添加了keepOneSelected
方法:如果可以禁用所有滑块,则没有任何一个具有剩余值。因此,该方法可确保至少一个复选框始终处于选中状态。
(根据评论中的讨论进行编辑:)
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ConnectedSlidersExt
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JSlider s0 = new JSlider(0, 100, 33);
JSlider s1 = new JSlider(0, 100, 33);
JSlider s2 = new JSlider(0, 100, 34);
int expectedSum = 100;
SliderGroup sliderGroup = new SliderGroup(expectedSum);
sliderGroup.add(s0);
sliderGroup.add(s1);
sliderGroup.add(s2);
JPanel panel =new JPanel(new GridLayout(0,3));
panel.add(s0);
panel.add(createListeningLabel(s0));
JCheckBox checkBox0 = createCheckBox(s0, sliderGroup);
panel.add(checkBox0);
panel.add(s1);
panel.add(createListeningLabel(s1));
JCheckBox checkBox1 = createCheckBox(s1, sliderGroup);
panel.add(checkBox1);
panel.add(s2);
panel.add(createListeningLabel(s2));
JCheckBox checkBox2 = createCheckBox(s2, sliderGroup);
panel.add(checkBox2);
keepOneSelected(checkBox0, checkBox1, checkBox2);
panel.add(createListeningLabel(s0, s1, s2));
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void keepOneSelected(JCheckBox ...checkBoxes)
{
ActionListener actionListener = new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
int numSelected = 0;
for (JCheckBox checkBox : checkBoxes)
{
if (checkBox.isSelected())
{
numSelected++;
}
}
if (numSelected == 1)
{
for (int i = 0; i < checkBoxes.length; i++)
{
JCheckBox checkBox = checkBoxes[i];
if (checkBox.isSelected())
{
checkBox.setEnabled(false);
}
}
}
else
{
for (int i = 0; i < checkBoxes.length; i++)
{
JCheckBox checkBox = checkBoxes[i];
checkBox.setEnabled(true);
}
}
}
};
for (JCheckBox checkBox : checkBoxes)
{
checkBox.addActionListener(actionListener);
}
}
private static JCheckBox createCheckBox(
JSlider slider, SliderGroup group)
{
JCheckBox checkBox = new JCheckBox();
checkBox.setSelected(true);
checkBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if (checkBox.isSelected())
{
slider.setEnabled(true);
group.addAndAdjust(slider);
}
else
{
slider.setEnabled(false);
group.removeAndAdjust(slider);
}
}
});
return checkBox;
}
private static JLabel createListeningLabel(final JSlider ... sliders)
{
final JLabel label = new JLabel("");
for (JSlider slider : sliders)
{
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
int sum = 0;
for (JSlider slider : sliders)
{
if (slider.isEnabled())
{
sum += slider.getValue();
}
}
label.setText("Sum: "+sum);
}
});
}
return label;
}
private static JLabel createListeningLabel(final JSlider slider)
{
final JLabel label = new JLabel("");
slider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
label.setText(String.valueOf(slider.getValue()));
}
});
return label;
}
}
class SliderGroup
{
private final int expectedSum;
private final LinkedList<JSlider> candidates;
private final ChangeListener changeListener;
private boolean updating = false;
SliderGroup(int expectedSum)
{
this.expectedSum = expectedSum;
this.candidates = new LinkedList<JSlider>();
changeListener = new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
JSlider source = (JSlider)e.getSource();
update(source);
}
};
}
private void update(JSlider source)
{
if (updating)
{
return;
}
updating = true;
for (JSlider slider : candidates)
{
slider.setValueIsAdjusting(true);
}
if (candidates.size() > 1)
{
int delta = computeSum() - expectedSum;
if (delta > 0)
{
distributeRemove(delta, source);
}
else
{
distributeAdd(delta, source);
}
}
for (JSlider slider : candidates)
{
slider.setValueIsAdjusting(false);
}
updating = false;
}
private void distributeRemove(int delta, JSlider source)
{
int counter = 0;
int remaining = delta;
while (remaining > 0)
{
//System.out.println("remove "+remaining);
JSlider slider = candidates.removeFirst();
counter++;
if (slider == source)
{
candidates.addLast(slider);
}
else
{
if (slider.getValue() > 0)
{
slider.setValue(slider.getValue()-1);
remaining--;
counter = 0;
}
candidates.addLast(slider);
if (remaining == 0)
{
break;
}
}
if (counter > candidates.size())
{
String message =
"Can not distribute " + delta + " among " + candidates;
// System.out.println(message);
// return;
throw new IllegalArgumentException(message);
}
}
}
private void distributeAdd(int delta, JSlider source)
{
int counter = 0;
int remaining = -delta;
while (remaining > 0)
{
//System.out.println("add "+remaining);
JSlider slider = candidates.removeLast();
counter++;
if (slider == source)
{
candidates.addFirst(slider);
}
else
{
if (slider.getValue() < slider.getMaximum())
{
slider.setValue(slider.getValue()+1);
remaining--;
counter = 0;
}
candidates.addFirst(slider);
if (remaining == 0)
{
break;
}
}
if (counter > candidates.size())
{
String message =
"Can not distribute " + delta + " among " + candidates;
// System.out.println(message);
// return;
throw new IllegalArgumentException(message);
}
}
}
private int computeSum()
{
int sum = 0;
for (JSlider slider : candidates)
{
sum += slider.getValue();
}
return sum;
}
void add(JSlider slider)
{
candidates.add(slider);
slider.addChangeListener(changeListener);
}
void remove(JSlider slider)
{
candidates.remove(slider);
slider.removeChangeListener(changeListener);
}
void addAndAdjust(JSlider slider)
{
add(slider);
if (candidates.size() == 2)
{
update(candidates.get(0));
}
else
{
update(slider);
}
}
void removeAndAdjust(JSlider slider)
{
remove(slider);
update(slider);
if (candidates.size() == 1)
{
JSlider candidate = candidates.get(0);
int max = candidate.getMaximum();
candidate.setValue(Math.min(max, expectedSum));
}
}
}