Pregunta

Estoy intentando imitar (o encontrar un componente preexistente) que imite el control deslizante de zoom de Word 2007:

 Barra de zoom de dos estados

Hay dos diferencias principales entre este componente y un Java JSlider estándar:

  1. No se ajusta a las marcas, excepto al 100%, y se ajusta mientras desliza la barra en lugar de soltar el mouse
  2. El control deslizante no es lineal en todo el camino: la mitad izquierda del control deslizante va del 10% al 100%; el lado derecho va del 100% al 500%.

Esto es lo que tengo hasta ahora: Clon de Java

Fuente:

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

}

Básicamente, solo imita la apariencia pero sin esas dos características mencionadas anteriormente. No veo nada en API de JSlider Eso me permite tener este comportamiento. ¿Tendría que empezar desde cero para obtener este comportamiento? Si es así, no vale mi tiempo, pero si alguien sabe de una buena manera de obtener este comportamiento, creo que sería bueno tenerlo en el proyecto en el que estoy trabajando.

Gracias,

Nick


EDITAR:

Gracias a PSpeed ??por la idea de solo asignar los 0..50 y 50..100 a diferentes valores. El código para hacerlo está debajo.

Lamentablemente, la idea de cambiar setValue a snap no funcionó.

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

Editar 2: Otra diferencia que observo es que un JButton no le permite mantenerlo presionado para disparar el botón varias veces, mientras que los botones +/- en la oficina sí lo hacen. ¿Alguna idea de cómo imitar esto?

¿Fue útil?

Solución

Creo que puedes obtener todo este comportamiento con un BoundedRangeModel personalizado. La clave es hacer que el modelo informe un tipo normal de rango de valores, pero tratarlo de manera diferente cuando desee su factor de zoom.

Por ejemplo, si deja que su rango se extienda de 0 a 100, trataría a 0-50 de una manera y 50-100 de otra manera (10-100% y 100-500% respectivamente).

Para obtener el comportamiento de ajuste, estoy bastante seguro de que solo puede anular setValue () para ajustar el rango que desee. Entonces, usando 0-100 como el rango de valores, si se llama a setValue () con 47-53, simplemente ajuste el valor a 50.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top