2011-10-01 50 views
4

我有N個JSliders的列表(N沒有程序上的變化,只是因爲我增加更多的功能。目前,N等於4)。所有滑塊值的總和必須等於100.當一個滑塊移動時,滑塊的其餘部分將進行調整。當滑塊被改變每個滑動器具有範圍從0到100。的Java JSlider的精度問題

值目前我使用這種邏輯(僞代碼):

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並滾動到頁面底部。

謝謝

p.s.將該值乘以比率可以讓用戶將值鎖定爲0.但是,當4個滑塊中的3個爲0,第4個滑塊爲100時,我不確定該做什麼,並且將第4個滑塊向下移動。使用上面的邏輯,3個滑塊的值爲0,第4個滑塊移動到用戶放置的位置,總數小於100,這是不正確的行爲。

EDIT

這裏是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); 
    } 
} 
+1

最好的辦法是建立一個[SSCCE(HTTP:// SSCCE .org)並在這裏發佈代碼。請在回覆之前閱讀鏈接,因爲我們絕對不想看到您的整個程序。 –

+0

請參閱下面的答案中的示例代碼。在Swing中, –

+0

,答案始終是一個自定義模型。儘管Slider並沒有像它應該那樣承認它:http://stackoverflow.com/questions/7468314/linked-jsliders-with-maximum-combined-value – kleopatra

回答

6

爲什麼不能讓讓他們從0到1000000的發言權JSlider的機型更細的粒度,以及具有和爲百萬?有了正確的Dictionary爲LabelTable,用戶可能不會知道,它不會從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; 
     } 
     } 

    } 

} 

編輯有SliderGroup2使用BoundedRangeModels的列表,而不是JSliders。

+0

一個吸引人的想法,但滑塊模擬的分辨率不會超過鼠標提供的分辨率。 – trashgod

+0

@trashgod:是的,你說得對。但是在我的SSCCE中,它似乎有些不錯。 –

+1

+1非常好!你平均分開考試。 「SLIDER_MAX_VALUE = 1000」和「total/10.0」將使箭頭有意義地移動拇指。順便說一句,我精確地混淆了分辨率。 – trashgod

2

滑塊會抽搐或不移動取決於比率值。

HumbleBundle有同樣的問題。如果您通過鍵盤移動滑塊,則該更改僅爲1,這意味着它只會移至第一個滑塊。所以你的比例最終會失去同步。

而且總價值並不總是加起來100

所以,你需要做一個舍入檢查。如果它不增加到100,那麼你需要決定錯誤發生在哪裏。也許最後一個滑塊給出了上述問題?

我不確定當4個滑塊中的3個爲0,第4個滑塊爲100時我該做什麼,而且我將第4個滑塊向下移動。

HumbleBundle處理它的方式是移動所有切片器。但是,僅允許您移動滑塊上下增量3,這樣就可以通過1

提高了每個對3個滑塊即使在HumbleBundle實現是不完美的。

+0

Humble的滑塊並不完美,但它們在任何時候都會達到100%。 – miningold

2

從一些Hovercrafts解決方案借用我想出了一種不同的方法。這種方法的基礎是在移動滑塊時跟蹤「其他滑條」值。只要您繼續滑動相同的滑塊,則凍結值將用於計算新值。然後任何四捨五入差異按順序應用到每個滑塊,直到差異用完。使用這種方法,您可以將滑塊的增量更改均勻地應用到所有其他滑塊。

在模型中的值是滑塊的實際值,也可以使用鍵盤來調整滑塊:

import java.awt.*; 
import java.awt.GridLayout; 
import java.util.ArrayList; 
import java.util.List; 

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


public class SliderGroup implements ChangeListener 
{ 
    private List<JSlider> sliders = new ArrayList<JSlider>(); 
    private int groupSum; 

    private boolean internalChange = false; 
    private JSlider previousSlider; 
    private List<SliderInfo> otherSliders = new ArrayList<SliderInfo>(); 

    public SliderGroup(int groupSum) 
    { 
     this.groupSum = groupSum; 
    } 

    public void addSlider(JSlider slider) 
    { 
     sliders.add(slider); 
     slider.addChangeListener(this); 
    } 

    @Override 
    public void stateChanged(ChangeEvent e) 
    { 
     if (internalChange) return; 

     internalChange = true; 
     JSlider sourceSlider = (JSlider)e.getSource(); 

     if (previousSlider != sourceSlider) 
     { 
      setupForSliding(sourceSlider); 
      previousSlider = sourceSlider; 
     } 

     int newSumOfOtherSliders = groupSum - sourceSlider.getValue(); 
     int oldSumOfOtherSliders = 0; 

     for (SliderInfo info : otherSliders) 
     { 
      JSlider slider = info.getSlider(); 

      if (slider != sourceSlider) 
      { 
       oldSumOfOtherSliders += info.getValue(); 
      } 
     } 

     int difference = newSumOfOtherSliders - oldSumOfOtherSliders; 

     if (oldSumOfOtherSliders == 0) 
     { 
      resetOtherSliders(difference/otherSliders.size()); 
      allocateDifference(difference % otherSliders.size(), true); 
      internalChange = false; 
      return; 
     } 

     double ratio = (double)newSumOfOtherSliders/oldSumOfOtherSliders; 

     for (SliderInfo info : otherSliders) 
     { 
       JSlider slider = info.getSlider(); 
       int oldValue = info.getValue(); 
       int newValue = (int)Math.round(oldValue * ratio); 
       difference += oldValue - newValue; 
       slider.getModel().setValue(newValue); 
     } 

     if (difference != 0) 
     { 
      allocateDifference(difference, false); 
     } 

     internalChange = false; 
    } 

    private void allocateDifference(int difference, boolean adjustZeroValue) 
    { 
     while (difference != 0) 
     { 
      for (SliderInfo info : otherSliders) 
      { 
       if (info.getValue() != 0 || adjustZeroValue) 
       { 
        JSlider slider = info.getSlider(); 

        if (difference > 0) 
        { 
         slider.getModel().setValue(slider.getValue() + 1); 
         difference--; 
        } 

        if (difference < 0) 
        { 
         slider.getModel().setValue(slider.getValue() - 1); 
         difference++; 
        } 
       } 
      } 
     } 
    } 

    private void resetOtherSliders(int resetValue) 
    { 
     for (SliderInfo info : otherSliders) 
     { 
      JSlider slider = info.getSlider(); 
      slider.getModel().setValue(resetValue); 
     } 
    } 

    private void setupForSliding(JSlider sourceSlider) 
    { 
     otherSliders.clear(); 

     for (JSlider slider: sliders) 
     { 
      if (slider != sourceSlider) 
      { 
       otherSliders.add(new SliderInfo(slider, slider.getValue())); 
      } 
     } 
    } 

    class SliderInfo 
    { 
     private JSlider slider; 
     private int value; 

     public SliderInfo(JSlider slider, int value) 
     { 
      this.slider = slider; 
      this.value = value; 
     } 

     public JSlider getSlider() 
     { 
      return slider; 
     } 

     public int getValue() 
     { 
      return value; 
     } 
    } 


    private static JPanel createSliderPanel(int groupSum, int sliderCount) 
    { 
     int sliderValue = groupSum/sliderCount; 

     SliderGroup sg = new SliderGroup(groupSum); 

     JPanel panel = new JPanel(new BorderLayout()); 

     JPanel sliderPanel = new JPanel(new GridLayout(0, 1)); 
     panel.add(sliderPanel, BorderLayout.CENTER); 

     JPanel labelPanel = new JPanel(new GridLayout(0, 1)); 
     panel.add(labelPanel, BorderLayout.EAST); 

     for (int i = 0; i < sliderCount; i++) 
     { 
      JLabel label = new JLabel(); 
      label.setText(Integer.toString(sliderValue)); 
      labelPanel.add(label); 

      JSlider slider = new JSlider(0, groupSum, sliderValue); 
      slider.setMajorTickSpacing(25); 
      slider.setMinorTickSpacing(5); 
      slider.setPaintTicks(true); 
      slider.setPaintLabels(true); 
      slider.setPaintTrack(true); 
      slider.addChangeListener(new LabelChangeListener(label)); 
      sliderPanel.add(slider); 

      sg.addSlider(slider); 
     } 

     return panel; 
    } 

    static class LabelChangeListener implements ChangeListener 
    { 
     private JLabel label; 

     public LabelChangeListener(JLabel label) 
     { 
      this.label = label; 
     } 

     @Override 
     public void stateChanged(ChangeEvent e) 
     { 
      JSlider slider = (JSlider)e.getSource(); 
      label.setText(Integer.toString(slider.getValue())); 
     } 
    } 

    private static void createAndShowGui() 
    { 
     JPanel panel = createSliderPanel(100, 5); 

     JFrame frame = new JFrame("SliderGroup"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.add(panel); 
     frame.pack(); 
     frame.setLocationByPlatform(true); 
     frame.setVisible(true); 
    } 

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

你的例子並不總是加起來爲100。如果第一個滑塊爲0,最下面的3個滑塊爲26,則可以將第二個滑塊從22移動到23,而不會使其他滑塊發生變化 – miningold

+0

它正在更新第一個滑塊,即使其初始值爲零這是一個問題。 – camickr