【问题标题】:Mimicking Zoom bar from Office 2007 in Java (Modified JSlider)在 Java 中模仿 Office 2007 中的缩放栏(修改后的 JSlider)
【发布时间】:2009-11-25 22:22:57
【问题描述】:

我正在尝试模仿(或找到一个预先存在的组件)来模仿 Word 2007 中的缩放滑块:

此组件与标准 Java JSlider 有两个主要区别:

  1. 在 100% 时不会捕捉到刻度,并且在滑动条时而不是在释放鼠标时捕捉
  2. 滑块在整个过程中不是线性的:滑块的左半部分从 10% 变为 100%;右侧从 100% 变为 500%。

这是我目前所拥有的:

来源:

    import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 *
 * @author NDUNN
 * @date Nov 25, 2009
 */
public class ZoomBar extends JPanel implements ActionListener, ChangeListener {

    private JLabel zoomAmount;
    private JButton minus;
    private JButton plus;
    private JSlider slider;

    private static final int MIN_ZOOM = 10;
    private static final int MAX_ZOOM = 200;
    private static final int DEFAULT_ZOOM = 100;

    private static final int MAJOR_ZOOM_SPACING = 50;
    private static final int MINOR_ZOOM_SPACING = 10;

    public ZoomBar() {
        super();

        minus = new JButton("-");
        plus = new JButton("+");
        slider = new JSlider(MIN_ZOOM, MAX_ZOOM, DEFAULT_ZOOM);

        slider.setMinorTickSpacing(MINOR_ZOOM_SPACING);
        slider.setMajorTickSpacing(MAJOR_ZOOM_SPACING);
        slider.setPaintTicks(true);
        slider.setSnapToTicks(true);

        zoomAmount = new JLabel(slider.getValue() + "%");

        add(zoomAmount);
        add(minus);
        add(slider);
        add(plus);

        plus.addActionListener(this);
        minus.addActionListener(this);

        slider.addChangeListener(this);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Zoom bar clone");
        frame.setContentPane(new ZoomBar());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == plus) {
            slider.setValue(slider.getValue() + MINOR_ZOOM_SPACING);
        }
        else if (e.getSource() == minus) {
            slider.setValue(slider.getValue() - MINOR_ZOOM_SPACING);
        }
    }

    public void stateChanged(ChangeEvent e) {
        if (slider.getValueIsAdjusting()) {
            return;
        }
        zoomAmount.setText(slider.getValue() + "%");
    }

}

基本上只是模仿外观,但没有上述两个功能。我在JSlider API 中看不到任何可以让我获得这种行为的东西。我是否必须从头开始才能获得这种行为?如果是这样,那不值得我花时间,但如果有人知道获得这种行为的好方法,我认为在我正在从事的项目中拥有它会很好。

谢谢,

尼克


编辑:

感谢 PSpeed 关于将 0..50 和 50..100 映射到不同值的想法。执行此操作的代码如下。

不幸的是,将 setValue 更改为 snap 的想法没有奏效。

public void stateChanged(ChangeEvent e) {
    // While slider is moving, snap it to midpoint
    int value = slider.getValue();
    if (slider.getValueIsAdjusting()) {
        return;
    }

    zoomValue = fromSlider(value);
    zoomLabel.setText(zoomValue + "%");
}

public int fromSlider(int sliderValue) {
    int mappedValue = 0;
    if (sliderValue <= 50) {
        // Map from [0, 50] to [MIN ... DEFAULT]
        mappedValue = (int) map(sliderValue, 0, 50, MIN_ZOOM, DEFAULT_ZOOM);
    }
    else {
        // Convert from  (50, 100] to (DEFAULT ... MAX]
        mappedValue = (int) map(sliderValue, 50, 100, DEFAULT_ZOOM, MAX_ZOOM);
    }
    return mappedValue;
}

public int toSlider(int modelValue) {
    int mappedValue = 0;
    if (modelValue <= DEFAULT_ZOOM) {
        // Map from [MIN_ZOOM, DEFAULT_ZOOM] to [0 ... 50]
        mappedValue = (int) map(modelValue, MIN_ZOOM, DEFAULT_ZOOM, 0, 50);
    }
    else {
        // Convert from  (DEFAULT ... MAX] to (50, 100]
        mappedValue = (int) map(modelValue, DEFAULT_ZOOM, MAX_ZOOM, 50, 100);
    }
    return mappedValue;
}


/**
 * @param value The incoming value to be converted
 * @param low1  Lower bound of the value's current range
 * @param high1 Upper bound of the value's current range
 * @param low2  Lower bound of the value's target range
 * @param high2 Upper bound of the value's target range
 * @return
 */
public static final double map(double value, double low1, double high1, double low2, double high2) {

    double diff = value - low1;
    double proportion = diff / (high1 - low1);

    return lerp(low2, high2, proportion);
}

public static final double lerp(double value1, double value2, double amt) {
    return ((value2 - value1) * amt) + value1;
}

编辑 2: 我注意到的另一个区别是 JButton 不允许您按住它多次触发按钮,而办公室中的 +/- 按钮可以。知道如何模仿吗?

【问题讨论】:

  • 你在哪里拍的?这个想法是在自定义有界范围模型内进行。理论是 JSlider 会尝试告诉你 47,但模型不会听,只是将自己的值设置为 50……不过,它仍然需要触发一个更改事件。这样 JSlider 就会知道它的拇指的正确位置是什么。由于用户仍在拖动,鼠标应该仍然在“47”位置,如果他们向左移动一点,那么它应该会捕捉。如果您描述实际发生的事情,那么也许我可以想到其他事情。
  • re:您的编辑 2,自定义 ButtonModel 应该能够处理... Swing 可能已经有一个他们在他们的微调器中使用但没有暴露。实际上,看起来他们在动作监听器中做了一些复杂的技巧。我仍然认为乍一看自定义按钮模型是最简单的。让 setPressed(true) 启动一个计时器,该计时器触发执行的操作并 setPressed(false) 停止计时器。添加初始延迟以使其感觉更好。
  • 我在 BoundedRangeModel 和 JSlider 本身中都进行了尝试。 pastebin.com/f10ddb17f 该值肯定是环绕的(我注意到它在我也在更新的标签中)但问题是拇指的位置与鼠标光标的位置紧密相关。
  • 如果是我,我可能会研究 JSlider 如何实现它自己的 snap-to-tick 功能,看看它是否提供了一种实现你想要的好方法的提示。它不同但又足够相似,可能会鼓舞人心。

标签: java user-interface swing jslider


【解决方案1】:

我相信您可以通过自定义 BoundedRangeModel 获得所有这些行为。关键是要让模型报告正常类型的值范围,但在需要缩放因子时以不同方式处理。

因此,例如,如果您让您的范围从 0 到 100,您会以一种方式对待 0-50,以另一种方式对待 50-100(分别为 10-100% 和 100-500%)。

为了获得捕捉行为,我很确定你可以重写 setValue() 来捕捉你想要的范围。所以使用 0-100 作为取值范围,如果用 47-53 调用 setValue() 则只需将值对齐到 50。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多