Java JSlider精度问题
我有一个 N JSlider 列表(N 在程序上不会改变,只有当我添加更多功能时才会改变。当前 N 等于 4)。所有滑块值的总和必须等于 100。当一个滑块移动时,其余滑块应进行调整。每个滑块的值范围为 0 到 100。
目前,当滑块更改时,我正在使用此逻辑(伪代码):
newValue = currentSlider.getValue
otherSliders = allSliders sans currentSlider
othersValue = summation of otherSliders values
properOthersValue = 100 - newValue
ratio = properOthersValue / othersValue
for slider in otherSlider
slider.value = slider.getValue * ratio
此设置的问题是滑块的值存储为整数。因此,当我调整滑块时,我遇到了精度问题:滑块会根据比率值抽动或根本不移动。另外,总价值并不总是等于 100。
有没有人可以解决这个问题,而无需创建一个支持浮点数或双精度数的全新 JSlider 类?
如果您想要我想要的行为示例,请访问:Humble Indie Bundle 并滚动到页面底部。
你
谢谢 将值乘以比率允许用户将值“锁定”为 0。但是,我不确定当 4 个滑块中的 3 个为 0 并且第 4 个滑块为 100 并且我将第 4 个滑块向下移动时该怎么办。使用上面的逻辑,值 0 的 3 个滑块保持原样,而第 4 个滑块移动到用户放置的位置,这使得总数小于 100,这是不正确的行为。
编辑
这是 SSCCE:
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.LinkedList;
public class SliderDemo
{
static LinkedList<JSlider> sliders = new LinkedList<JSlider>();
static class SliderListener implements ChangeListener
{
boolean updating = false;
public void stateChanged(ChangeEvent e)
{
if (updating) return;
updating = true;
JSlider source = (JSlider)e.getSource();
int newValue = source.getValue();
LinkedList<JSlider> otherSliders = new LinkedList<JSlider>(sliders);
otherSliders.remove(source);
int otherValue = 0;
for (JSlider slider : otherSliders)
{
otherValue += slider.getValue();
}
int properValue = 100 - newValue;
double ratio = properValue / (double)otherValue;
for (JSlider slider : otherSliders)
{
int currentValue = slider.getValue();
int updatedValue = (int) (currentValue * ratio);
slider.setValue(updatedValue);
}
int total = 0;
for (JSlider slider : sliders)
{
total += slider.getValue();
}
System.out.println("Total = " + total);
updating = false;
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("SliderDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
JPanel sliderPanel = new JPanel(new GridBagLayout());
container.add(sliderPanel);
SliderListener listener = new SliderListener();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
int sliderCount = 4;
int initial = 100 / sliderCount;
for (int i = 0; i < sliderCount; i++)
{
gbc.gridy = i;
JSlider slider = new JSlider(0, 100, initial);
slider.addChangeListener(listener);
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
sliders.add(slider);
sliderPanel.add(slider, gbc);
}
frame.pack();
frame.setVisible(true);
}
}
I have a list of N JSliders (N does not change procedurally, only as I add more features. Currently N equals 4). The sum of all the sliders values must equal to 100. As one slider moves the rest of the sliders shall adjust. Each slider has values that range from 0 to 100.
Currently I am using this logic when a slider is changed (pseudo-code):
newValue = currentSlider.getValue
otherSliders = allSliders sans currentSlider
othersValue = summation of otherSliders values
properOthersValue = 100 - newValue
ratio = properOthersValue / othersValue
for slider in otherSlider
slider.value = slider.getValue * ratio
The problem with this setup is slider's values are stored as ints. So as I adjust the sliders I get precision problems: sliders will twitch or not move at all depending on the ratio value. Also the total value does not always add up to 100.
Does anyone have a solution to this problem without creating an entirely new JSlider class that supports floats or doubles?
If you want an example of the behavior I want, visit: Humble Indie Bundle and scroll to the bottom of the page.
thank you
p.s. Multiplying the values by the ratio allows for the user to 'lock' values at 0. However, I am not sure what to do when 3 of the 4 sliders are at 0 and the 4th slider is at 100 and I move the 4th slider down. Using the logic above, the 3 sliders with 0 as their value stay put and the 4th slider moves to where the user puts it, which makes the total less than 100, which is improper behavior.
EDIT
Here is the SSCCE:
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.LinkedList;
public class SliderDemo
{
static LinkedList<JSlider> sliders = new LinkedList<JSlider>();
static class SliderListener implements ChangeListener
{
boolean updating = false;
public void stateChanged(ChangeEvent e)
{
if (updating) return;
updating = true;
JSlider source = (JSlider)e.getSource();
int newValue = source.getValue();
LinkedList<JSlider> otherSliders = new LinkedList<JSlider>(sliders);
otherSliders.remove(source);
int otherValue = 0;
for (JSlider slider : otherSliders)
{
otherValue += slider.getValue();
}
int properValue = 100 - newValue;
double ratio = properValue / (double)otherValue;
for (JSlider slider : otherSliders)
{
int currentValue = slider.getValue();
int updatedValue = (int) (currentValue * ratio);
slider.setValue(updatedValue);
}
int total = 0;
for (JSlider slider : sliders)
{
total += slider.getValue();
}
System.out.println("Total = " + total);
updating = false;
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("SliderDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = frame.getContentPane();
JPanel sliderPanel = new JPanel(new GridBagLayout());
container.add(sliderPanel);
SliderListener listener = new SliderListener();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
int sliderCount = 4;
int initial = 100 / sliderCount;
for (int i = 0; i < sliderCount; i++)
{
gbc.gridy = i;
JSlider slider = new JSlider(0, 100, initial);
slider.addChangeListener(listener);
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
sliders.add(slider);
sliderPanel.add(slider, gbc);
}
frame.pack();
frame.setVisible(true);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
为什么不让 JSlider 模型的粒度更细,比如让它们从 0 到 1000000,并且总和为 1000000?使用 LabelTable 的正确
Dictionary
,用户可能不知道它不是从 0 到 100。例如:
编辑为让 SliderGroup2 使用 BoundedRangeModels 列表而不是 JSliders。
Why not making the granularity of the JSlider models finer by say having them go from 0 to 1000000, and having the sum be 1000000? With the proper
Dictionary
for the LabelTable, the user will probably not know that it doesn't go from 0 to 100.For example:
Edited to have SliderGroup2 use a List of BoundedRangeModels rather than JSliders.
HumbleBundle 也有同样的问题。如果您通过键盘移动滑块,则更改仅为 1,这意味着它只会转到第一个滑块。所以你的比率最终会变得不同步。
因此您需要进行四舍五入检查。如果加起来没有达到 100,那么您需要确定错误出在哪里。也许最后一个滑块考虑到了上述问题?
HumbleBundle 处理它的方式是移动所有切片器。然而,它只允许您将滑块向下移动 3 个增量,因此您可以将 3 个滑块中的每一个增加 1。
即使 HumbleBundle 的实现也并不完美。
HumbleBundle has the same problem. If you move the slider by the keyboard then the change is only 1, which means it will only ever go to the first slider. So you ratios will eventually get out of sync.
So you need to do a rounding check. If it doesn't add to 100, then you need to decide where the error goes. Maybe the last slider given the above problem?
The way HumbleBundle handles it is to move all the slicers. However it only allows you to move the slider down increments of 3, so that you can increase each of the 3 sliders by 1.
Even the implementation at HumbleBundle isn't perfect.
借鉴气垫船的一些解决方案,我想出了一种不同的方法。这种方法的基础是在移动滑块时跟踪“其他滑块”值。只要您继续滑动同一滑块,冻结的值就会用于计算新值。然后,所有舍入差异将按顺序应用于每个滑块,直到差异用完。使用这种方法,您可以将滑块中的增量更改均匀地应用于所有其他滑块。
模型中的值是滑块的实际值,您也可以使用键盘来调整滑块:
Borrowing from some of Hovercrafts solution I came up with a different approach. The basis of this approach is that the "other sliders" values are tracked at the time a slider is moved. As long as you continue to slide the same slider the frozen values are used to calculate the new values. Any rounding differences are then applied sequentially to each slider until the difference is used up. Using this approach you can have incremental changes in the slider applied evenly to all of the other sliders.
The values in the model are the actual values of the slider and you can also use the keyboard to adjust the sliders: