4

N 個の JSliders のリストがあります (N は手続き的には変更されません。機能を追加するときだけです。現在、N は 4 です)。すべてのスライダー値の合計は 100 に等しくなければなりません。1 つのスライダーが移動すると、残りのスライダーが調整されます。各スライダーには、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

このセットアップの問題は、スライダーの値が int として格納されることです。そのため、スライダーを調整すると、精度の問題が発生します。比率の値に応じて、スライダーがけいれんしたり、まったく動かなかったりします。また、合計値が常に 100 になるとは限りません。

float または double をサポートするまったく新しい JSlider クラスを作成せずに、この問題を解決できる人はいますか?

私が望む動作の例が必要な場合は、Humble Indie Bundleにアクセスし、ページの一番下までスクロールしてください。

ありがとうございました

ps 比率で値を乗算すると、ユーザーは値を 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);
    }
}
4

3 に答える 3

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;
         }
      }

   }

}

SliderGroup2 が JSliders ではなく BoundedRangeModels のリストを使用するように編集しました。

于 2011-10-01T02:45:42.290 に答える
2

比率の値に応じて、スライダーがけいれんしたり、まったく動かなかったりします。

HumbleBundle にも同じ問題があります。キーボードでスライダーを動かした場合、変化は 1 だけです。つまり、最初のスライダーにしか移動しません。したがって、比率は最終的に同期しなくなります。

また、合計値が常に 100 になるとは限りません。

したがって、丸めチェックを行う必要があります。合計しても 100 にならない場合は、エラーの原因を特定する必要があります。上記の問題を考えると、最後のスライダーでしょうか?

4 つのスライダーのうち 3 つが 0 で、4 番目のスライダーが 100 で、4 番目のスライダーを下に移動した場合、どうすればよいかわかりません。

HumbleBundle がそれを処理する方法は、すべてのスライサーを移動することです。ただし、スライダーを 3 ずつ下に移動することしかできないため、3 つのスライダーのそれぞれを 1 ずつ増やすことができます。

HumbleBundle での実装も完璧ではありません。

于 2011-10-01T02:11:06.173 に答える
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();
            }
        });
    }
}
于 2011-10-02T00:57:53.947 に答える