Java JSlider精度问题

min*_*old 4 java swing jslider

我有一个N JSliders列表(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
Run Code Online (Sandbox Code Playgroud)

此设置的问题是滑块的值存储为整数.因此,当我调整滑块时,我会遇到精度问题:根据比率值,滑块会根据或不移动.此外,总值并不总是达到100.

有没有人有一个解决这个问题的方法而不创建一个支持浮点数或双打的全新JSlider类?

如果您想要我想要的行为示例,请访问:Humble Indie Bundle并滚动到页面底部.

谢谢

ps将值乘以比率允许用户将值"锁定"为0.但是,当4个滑块中的3个为0且第4个滑块为100且我移动第4个滑块时,我不确定该怎么办下.使用上面的逻辑,3个滑块以0为值保持不变,第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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Hov*_*els 6

为什么不让JSlider模型的粒度从0到1000000变得更精细,并且总和为1000000?对于DictionaryLabelTable,用户可能不知道它不会从0变为100.

例如:

import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

@SuppressWarnings("serial")
public class LinkedSliders2 extends JPanel {
   private static final int SLIDER_COUNT = 5;
   public static final int SLIDER_MAX_VALUE = 1000;
   private static final int MAJOR_TICK_DIVISIONS = 5;
   private static final int MINOR_TICK_DIVISIONS = 20;
   private static final int LS_WIDTH = 700;
   private static final int LS_HEIGHT = 500;
   private JSlider[] sliders = new JSlider[SLIDER_COUNT];
   private SliderGroup2 sliderGroup = new SliderGroup2(SLIDER_MAX_VALUE);

   public LinkedSliders2() {
      Dictionary<Integer, JComponent> myDictionary = new Hashtable<Integer, JComponent>();
      for (int i = 0; i <= MAJOR_TICK_DIVISIONS; i++) {
         Integer key = i * SLIDER_MAX_VALUE / MAJOR_TICK_DIVISIONS;
         JLabel value = new JLabel(String.valueOf(i * 100 / MAJOR_TICK_DIVISIONS));
         myDictionary.put(key, value);
      }
      setLayout(new GridLayout(0, 1));
      for (int i = 0; i < sliders.length; i++) {
         sliders[i] = new JSlider(0, SLIDER_MAX_VALUE, SLIDER_MAX_VALUE
               / SLIDER_COUNT);
         sliders[i].setLabelTable(myDictionary );
         sliders[i].setMajorTickSpacing(SLIDER_MAX_VALUE / MAJOR_TICK_DIVISIONS);
         sliders[i].setMinorTickSpacing(SLIDER_MAX_VALUE / MINOR_TICK_DIVISIONS);
         sliders[i].setPaintLabels(true);
         sliders[i].setPaintTicks(true);
         sliders[i].setPaintTrack(true);
         sliderGroup.addSlider(sliders[i]);
         add(sliders[i]);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(LS_WIDTH, LS_HEIGHT);
   }

   private static void createAndShowGui() {
      LinkedSliders2 mainPanel = new LinkedSliders2();

      JFrame frame = new JFrame("LinkedSliders");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class SliderGroup2 {
   private List<BoundedRangeModel> sliderModelList = new ArrayList<BoundedRangeModel>();
   private ChangeListener changeListener = new SliderModelListener();
   private int maxValueSum;

   public SliderGroup2(int maxValueSum) {
      this.maxValueSum = maxValueSum;
   }

   public void addSlider(JSlider slider) {
      BoundedRangeModel model = slider.getModel();
      sliderModelList.add(model);
      model.addChangeListener(changeListener);
   }

   private class SliderModelListener implements ChangeListener {
      private boolean internalChange = false;

      @Override
      public void stateChanged(ChangeEvent cEvt) {
         if (!internalChange) {
            internalChange = true;
            BoundedRangeModel sourceModel = (BoundedRangeModel) cEvt.getSource();
            int sourceValue = sourceModel.getValue();

            int oldSumOfOtherSliders = 0;
            for (BoundedRangeModel model : sliderModelList) {
               if (model != sourceModel) {
                  oldSumOfOtherSliders += model.getValue();
               }
            }
            if (oldSumOfOtherSliders == 0) {
               for (BoundedRangeModel model : sliderModelList) {
                  if (model != sourceModel) {
                     model.setValue(1);
                  }
               }
               internalChange = false;
               return;
            }

            int newSumOfOtherSliders = maxValueSum - sourceValue;

            for (BoundedRangeModel model : sliderModelList) {
               if (model != sourceModel) {
                  long newValue = ((long) newSumOfOtherSliders * model
                        .getValue()) / oldSumOfOtherSliders;
                  model.setValue((int) newValue);
               }
            }

            int total = 0;
            for (BoundedRangeModel model : sliderModelList) {
               total += model.getValue();
            }
            //!! System.out.printf("Total = %.0f%n", (double)total * 100 / LinkedSliders2.SLIDER_MAX_VALUE);

            internalChange = false;
         }
      }

   }

}
Run Code Online (Sandbox Code Playgroud)

编辑让SliderGroup2使用BoundedRangeModel而不是JSliders.