用 Java 模仿 Office 2007 的缩放栏(修改后的 JSlider)

发布于 2024-08-13 03:30:35 字数 4949 浏览 8 评论 0原文

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

双状态缩放栏

该组件与标准 Java JSlider 之间有两个主要区别:

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

这是我到目前为止所拥有的: Java 克隆

资料来源:

    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 不允许您多次按住它来触发按钮,而办公室中的 +/- 按钮则可以。知道如何模仿这个吗?

I'm attempting to mimic (or find a preexisting component) that mimics the zoom slider from Word 2007:

Two state zoom bar

There are two main differences between this component and a standard Java JSlider:

  1. Does not snap to ticks except at 100%, and snaps while you're sliding the bar rather than when you release mouse
  2. Slider is not linear the whole way across: The left half of slider goes from 10% to 100%; the right side goes from 100% to 500%.

Here is what I have so far:
Java clone

Source:

    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() + "%");
    }

}

Basically just mimicking the look but without those two features mentioned above. I don't see anything in the JSlider API that allows me to get this behavior. Would I have to start from scratch to get this behavior? If so it's not worth my time, but if anyone knows of a good way to get this behavior, I think it'd be nice to have in the project I'm working on.

Thanks,

Nick


EDIT:

Thanks to PSpeed for idea about just mapping the 0..50 and 50..100 to different values. Code to do so is below.

Unfortunately the idea about changing setValue to snap did not work.

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

Edit 2:
One other difference I notice is that a JButton doesn't let you hold it down to fire button multiple times, whereas the +/- buttons in office do. Any idea how to mimic this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

够钟 2024-08-20 03:30:35

我相信您可以通过自定义 BoundedRangeModel 获得所有这些行为。关键是使模型报告正常类型的值范围,但当您需要缩放系数时以不同的方式对待它。

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

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

I believe you can get all of this behavior with a custom BoundedRangeModel. The key is to make the model report a normal type of range of values but treat it differently when you want your zoom factor.

So for example if you let your range run from 0 - 100, you'd treat 0-50 one way and 50-100 another way (10-100% and 100-500% respectively).

To get the snap behavior, I'm pretty sure you can just override setValue() to snap the range you want. So using 0-100 as the value range, if setValue() is called with 47-53 then just snap the value to 50.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文