Имитация панели масштабирования из Office 2007 на Java (модифицированный JSlider)

StackOverflow https://stackoverflow.com/questions/1800337

Вопрос

Я пытаюсь имитировать (или найти ранее существующий компонент), который имитирует ползунок масштабирования из Word 2007:

Two state zoom bar

Есть два основных отличия между этим компонентом и стандартным Java JSlider:

  1. Не привязывается к отметкам, за исключением 100%, и привязывается, когда вы перемещаете панель, а не когда отпускаете мышь
  2. Слайдер не является линейным по всей длине:Левая половина ползунка увеличивается с 10% до 100%;правая сторона увеличивается со 100% до 500%.

Вот что у меня есть на данный момент:Java clone

Источник:

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

}

По сути, просто имитирует внешний вид, но без этих двух функций, упомянутых выше.Я ничего не вижу в API JSlider это позволяет мне добиться такого поведения.Должен ли я начинать с нуля, чтобы добиться такого поведения?Если это так, то это не стоит моего времени, но если кто-нибудь знает хороший способ добиться такого поведения, я думаю, это было бы неплохо использовать в проекте, над которым я работаю.

Спасибо,

Ник


Редактировать:

Спасибо 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 не позволяет вам удерживать нажатой кнопку запуска несколько раз, в то время как кнопки + / - в office это делают.Есть идеи, как это имитировать?

Это было полезно?

Решение

Я полагаю, что вы можете получить все это поведение с помощью пользовательской BoundedRangeModel.Ключ в том, чтобы сделать отчет модели обычным типом диапазона значений, но относиться к нему по-другому, когда вам нужен свой коэффициент масштабирования.

Так, например, если вы разрешите своему диапазону работать от 0 до 100, вы будете обрабатывать 0-50 одним способом, а 50-100 - другим (10-100% и 100-500% соответственно).

Чтобы получить поведение привязки, я почти уверен, что вы можете просто переопределить setValue(), чтобы привязать нужный диапазон.Таким образом, используя 0-100 в качестве диапазона значений, если setValue() вызывается с 47-53, то просто привязывайте значение к 50.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top