Question

J'essaie d'imiter (ou de trouver un composant préexistant) reproduisant le curseur de zoom de Word 2007:

 Barre de zoom à deux états

Il existe deux différences principales entre ce composant et un JSlider Java standard:

  1. Ne s'aligne pas sur les ticks, sauf à 100%, et s'accroche lorsque vous faites glisser la barre plutôt que lorsque vous relâchez la souris
  2. Le curseur n'est pas linéaire dans son ensemble: la moitié gauche du curseur passe de 10% à 100%; le côté droit passe de 100% à 500%.

Voici ce que j'ai jusqu'à présent: Clone Java

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

}

En gros, imitez l'apparence, mais sans les deux caractéristiques mentionnées ci-dessus. Je ne vois rien dans API JSlider . cela me permet d’obtenir ce comportement. Devrais-je partir de zéro pour avoir ce comportement? Si c'est le cas, cela ne vaut pas la peine, mais si quelqu'un connaît un bon moyen d'obtenir ce comportement, je pense que ce serait bien d'avoir le projet sur lequel je travaille.

Merci,

Nick

EDIT:

Merci à PSpeed ??d’avoir eu l’idée de mapper les valeurs 0..50 et 50..100 sur différentes valeurs. Le code pour le faire est ci-dessous.

Malheureusement, l'idée de changer setValue en snap n'a pas fonctionné.

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

Éditer 2: Une autre différence que j'ai remarquée est qu'un JButton ne vous permet pas de le maintenir enfoncé plusieurs fois, alors que les boutons +/- du bureau le permettent. Avez-vous une idée de comment imiter cela?

Était-ce utile?

La solution

Je pense que vous pouvez obtenir tout ce comportement avec un BoundedRangeModel personnalisé. La clé est de faire en sorte que le modèle rapporte un type de plage de valeurs normal, mais le traite différemment lorsque vous souhaitez que votre facteur de zoom

Ainsi, par exemple, si vous laissez votre plage de 0 à 100, vous traitez 0 à 50 dans un sens et 50 à 100 dans un autre sens (10 à 100% et 100 à 500% respectivement).

Pour obtenir le comportement d'accrochage, je suis sûr que vous pouvez simplement remplacer setValue () pour capturer la plage souhaitée. Donc, en utilisant 0-100 comme plage de valeurs, si setValue () est appelée avec 47-53, alignez simplement la valeur sur 50.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top