当前位置: 首页 > 知识库问答 >
问题:

如何防止JSlider全范围移动?

梁丘威
2023-03-14

在这方面花费了太多时间后,我认为根本问题在于我在一个无意的使用模型中误用/滥用了JSlider。限制拇指的移动范围小于BoundedRangeModel范围需要修改BasicSliderUI类。虽然下面aterai提出的原始解决方案确实有效,但它需要重写一个SliderUI实现类,这会影响PLAF的可移植性和一致性。因此,要么我必须找到一个不同的UI元素,要么根据其他相互依赖的变量值修改JSlider BoundedRangeModel限制。后者的缺点是,基于其他用户可编辑参数的值,相同的拇指位置将表示不同的值。

共有1个答案

毋树
2023-03-14

您可以重写MetalSliderUICreateTrackListener(...)方法,以防止这种拖动。

编辑

  • 另一种选择是使用JLayer(未经测试的代码,可能需要一些自定义才能用于其他Lookandfeel):
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicSliderUI;
// import javax.swing.plaf.metal.MetalSliderUI;
// import javax.swing.plaf.synth.SynthSliderUI;
// import com.sun.java.swing.plaf.windows.WindowsSliderUI;

public class DragLimitedSliderTest {
  private static int MAXI = 80;
  private JComponent makeUI() {
    JSlider slider1 = makeSlider();
    JSlider slider2 = makeSlider();
    slider2.setUI(new BasicSliderUI(slider2) {
    //slider2.setUI(new WindowsSliderUI(slider2) {
    //slider2.setUI(new MetalSliderUI() {
    //slider2.setUI(new SynthSliderUI(slider2) {
      @Override protected TrackListener createTrackListener(JSlider slider) {
        return new TrackListener() {
          @Override public void mouseDragged(MouseEvent e) {
            //case HORIZONTAL:
            int halfThumbWidth = thumbRect.width / 2;
            int thumbLeft = e.getX() - offset;
            int maxPos = xPositionForValue(MAXI) - halfThumbWidth;
            if (thumbLeft > maxPos) {
              int x = maxPos + offset;
              MouseEvent me = new MouseEvent(
                e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(),
                x, e.getY(),
                e.getXOnScreen(), e.getYOnScreen(),
                e.getClickCount(), e.isPopupTrigger(), e.getButton());
              e.consume();
              super.mouseDragged(me);
            } else {
              super.mouseDragged(e);
            }
          }
        };
      }
    });
    JSlider slider3 = makeSlider();

    JPanel p = new JPanel(new GridLayout(3, 1));
    p.add(slider1);
    p.add(slider2);
    p.add(new JLayer<JSlider>(slider3, new DisableInputLayerUI()));
    return p;
  }
  private static JSlider makeSlider() {
    JSlider slider = new JSlider(0, 100, 40) {
      @Override public void setValue(int n) {
        super.setValue(n);
      }
    };
    slider.setMajorTickSpacing(10);
    slider.setPaintTicks(true);
    slider.setPaintLabels(true);
    Dictionary dictionary = slider.getLabelTable();
    if (dictionary != null) {
      Enumeration elements = dictionary.elements();
      while (elements.hasMoreElements()) {
        JLabel label = (JLabel) elements.nextElement();
        int v = Integer.parseInt(label.getText());
        if (v > MAXI) {
          label.setForeground(Color.RED);
        }
      }
    }
    slider.getModel().addChangeListener(new ChangeListener() {
      @Override public void stateChanged(ChangeEvent e) {
        BoundedRangeModel m = (BoundedRangeModel) e.getSource();
        if (m.getValue() > MAXI) {
          m.setValue(MAXI);
        }
      }
    });
    return slider;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
//         for (UIManager.LookAndFeelInfo laf: UIManager.getInstalledLookAndFeels()) {
//           if ("Nimbus".equals(laf.getName())) {
//             UIManager.setLookAndFeel(laf.getClassName());
//           }
//         }
      } catch (Exception e) {
        e.printStackTrace();
      }
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new DragLimitedSliderTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class DisableInputLayerUI extends LayerUI<JSlider> {
  @Override public void installUI(JComponent c) {
    super.installUI(c);
    if (c instanceof JLayer) {
      JLayer jlayer = (JLayer) c;
      jlayer.setLayerEventMask(
        AWTEvent.MOUSE_EVENT_MASK |
        AWTEvent.MOUSE_MOTION_EVENT_MASK);
    }
  }
  @Override public void uninstallUI(JComponent c) {
    if (c instanceof JLayer) {
      JLayer jlayer = (JLayer) c;
      jlayer.setLayerEventMask(0);
    }
    super.uninstallUI(c);
  }
  private Rectangle thumbRect = new Rectangle(11, 19); //magic number
  private Rectangle focusRect = new Rectangle();
  private Rectangle contentRect = new Rectangle();
  private Rectangle trackRect = new Rectangle();
  private int offset;

  protected int xPositionForValue(JSlider slider, int value) {
    int min = slider.getMinimum();
    int max = slider.getMaximum();
    int trackLength = trackRect.width;
    double valueRange = (double) max - (double) min;
    double pixelsPerValue = (double) trackLength / valueRange;
    int trackLeft = trackRect.x;
    int trackRight = trackRect.x + (trackRect.width - 1);
    int xPosition;

    xPosition = trackLeft;
    xPosition += Math.round(pixelsPerValue * ((double) value - min));

    xPosition = Math.max(trackLeft, xPosition);
    xPosition = Math.min(trackRight, xPosition);

    return xPosition;
  }
  protected int getHeightOfTallestLabel(JSlider slider) {
    Dictionary dictionary = slider.getLabelTable();
    int tallest = 0;
    if (dictionary != null) {
      Enumeration keys = dictionary.keys();
      while (keys.hasMoreElements()) {
        JComponent label = (JComponent) dictionary.get(keys.nextElement());
        tallest = Math.max(label.getPreferredSize().height, tallest);
      }
    }
    return tallest;
  }
  @Override protected void processMouseEvent(MouseEvent e, JLayer<? extends JSlider> l) {
    JSlider slider = l.getView();
    if (e.getID() == MouseEvent.MOUSE_PRESSED) {
      //case HORIZONTAL:

      //recalculateIfInsetsChanged()
      Insets insetCache = slider.getInsets();
      Insets focusInsets = UIManager.getInsets("Slider.focusInsets");
      if (focusInsets == null) {
        focusInsets = new Insets(2, 2, 2, 2); //magic number
      }

      //calculateFocusRect()
      focusRect.x = insetCache.left;
      focusRect.y = insetCache.top;
      focusRect.width = slider.getWidth() - (insetCache.left + insetCache.right);
      focusRect.height = slider.getHeight() - (insetCache.top + insetCache.bottom);

      //calculateContentRect()
      contentRect.x = focusRect.x + focusInsets.left;
      contentRect.y = focusRect.y + focusInsets.top;
      contentRect.width = focusRect.width - (focusInsets.left + focusInsets.right);
      contentRect.height = focusRect.height - (focusInsets.top + focusInsets.bottom);

      //calculateThumbSize()
      Icon ti = UIManager.getIcon("Slider.horizontalThumbIcon");
      if (ti != null) {
        thumbRect.width = ti.getIconWidth();
        thumbRect.height = ti.getIconHeight();
      }

      //calculateTrackBuffer()
      int trackBuffer = 9; //magic number, Windows: 9, Metal: 10 ...

      //calculateTrackRect()
      int centerSpacing = thumbRect.height;
      if (slider.getPaintTicks())  centerSpacing += 8; //magic number getTickLength();
      if (slider.getPaintLabels()) centerSpacing += getHeightOfTallestLabel(slider);
      trackRect.x = contentRect.x + trackBuffer;
      trackRect.y = contentRect.y + (contentRect.height - centerSpacing - 1) / 2;
      trackRect.width = contentRect.width - (trackBuffer * 2);
      trackRect.height = thumbRect.height;

      //calculateThumbLocation()
      int valuePosition = xPositionForValue(slider, slider.getValue());
      thumbRect.x = valuePosition - (thumbRect.width / 2);
      thumbRect.y = trackRect.y;
      offset = e.getX() - thumbRect.x;
    }
  }
  @Override protected void processMouseMotionEvent(MouseEvent e, JLayer<? extends JSlider> l) {
    if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
      JSlider slider = l.getView();
      //case HORIZONTAL:
      int halfThumbWidth = thumbRect.width / 2;
      int thumbLeft = e.getX() - offset;
      int maxPos = xPositionForValue(slider, 80) - halfThumbWidth;
      if (thumbLeft > maxPos) {
        e.consume();
        SliderUI ui = slider.getUI();
        if (ui instanceof BasicSliderUI) {
          ((BasicSliderUI) ui).setThumbLocation(maxPos, thumbRect.y);
        }
        slider.getModel().setValue(80);
      }
    }
  }
}
 类似资料:
  • 我正在用java编写一些代码,它检查一个由51个对象组成的数组,名为,这些对象是从右向左移动到x:200px位置的播放器的圆圈。我想使它这样,玩家被判分或扣分的基础上,他们的按钮按下的时间与一个节拍的位置相比,到目前为止,我有以下几个: 这种方法会导致几个问题,因为范围重叠,以此类推,按下按钮通常会输出如下内容: 然而,我希望它一次只检查一个位置,例如最靠近播放器的位置,或者是迭代器,用于检查数组

  • 问题内容: 我在HTML的多个位置使用指令。每个人都为自己创建了一个孤立的范围,这显然给我带来了很多麻烦。 例如。 在上面提到的html中,正在创建4个作用域。默认情况下,在定义的父级中创建1个,默认情况下创建3个。 是否有可能阻止自身创建隔离范围?如果不可能,那么是否有解决方法? 问题答案: 您需要创建自己的指令,该指令可以在没有隔离范围的情况下加载指定的模板。 的HTML 指示 工作朋克

  • 对易失性字段的写和读分别防止了在易失性字段之前和之后的读/写的重新排序。在写到易失性变量之前的变量读/写不能被重新排序为在它之后发生,在从易失性变量读到之后的读/写不能被重新排序为在它之前发生。但是这种禁止的范围是什么呢?正如我所理解的,volatile变量只能在使用它的块内防止重新排序,对吗? 为了清楚起见,让我举一个具体的例子。假设我们有这样的代码: 让我再举一个具体的例子来说明范围以澄清事情

  • 问题内容: 如何在解决方案中全局禁止StyleCop警告? 该解决方案使用Jenkins(连续构建和集成工具)不断构建,并应用了所有StyleCop规则。该解决方案使用TAB字符而不是4个空格,因为这是我的开发团队所采用的标准。因此,StlyeCop会引发几个SA1027警告。 如何从詹金斯删除SA1027警告?那也有帮助。 在每个C#文件上提供SuppressMessage看起来并不好。这就是为

  • 我正在使用不同作用域的bean开发一个Spring应用程序。许多bean是单例的,有其他请求或自定义的作用域。特别是,使用这些自定义作用域有时很难找出哪个作用域可以安全地注入到其他作用域中,或者何时需要使用。 我知道我可以为所有基本上不是单例的bean创建范围代理,但在许多情况下,这似乎是不必要的。例如,一个bean可能只应该被注入到相同范围的其他bean,但并不是每个在项目中工作的人都知道这一点

  • 我使用的是——保留类和——保留类成员,但这些似乎只保留类中的方法名称和字段,我想告诉Proproaurd不要碰特定方法内部的任何东西,即使是“for”循环或“if”语句,甚至变量名称!,我想看到一个方法在反编译后看起来与原始版本完全相同,就像您在调试模式下反编译程序时一样!,但只有一种方法而不是所有其他方法。 有没有办法使用proGuard做到这一点? 谢谢:)