Domanda

Sto tentando di imitare (o trovare un componente preesistente) che imita il dispositivo di scorrimento dello zoom da Word 2007:

 Barra dello zoom a due stati

Esistono due differenze principali tra questo componente e un JSlider Java standard:

  1. Non si aggancia ai segni di spunta eccetto al 100% e scatta mentre si fa scorrere la barra anziché quando si rilascia il mouse
  2. Il cursore non è lineare per tutto il percorso: la metà sinistra del cursore va dal 10% al 100%; la parte destra va dal 100% al 500%.

Ecco quello che ho finora: Java clone

Fonte:

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

}

Fondamentalmente solo imitando il look ma senza quelle due caratteristiche sopra menzionate. Non vedo nulla nella API JSlider che mi permette di ottenere questo comportamento. Dovrei ricominciare da zero per ottenere questo comportamento? Se è così non vale la pena, ma se qualcuno conosce un buon modo per ottenere questo comportamento, penso che sarebbe bello avere nel progetto a cui sto lavorando.

Grazie,

Nick


EDIT:

Grazie a PSpeed ??per l'idea di mappare solo 0..50 e 50..100 su valori diversi. Il codice per farlo è sotto.

Sfortunatamente l'idea di cambiare setValue in snap non ha funzionato.

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

Modifica 2: Un'altra differenza che noto è che un JButton non ti consente di tenerlo premuto per sparare più volte, mentre i pulsanti +/- in ufficio lo fanno. Qualche idea su come imitare questo?

È stato utile?

Soluzione

Credo che tu possa ottenere tutto questo comportamento con un BoundedRangeModel personalizzato. La chiave è fare in modo che il modello segnali un normale tipo di intervallo di valori ma lo tratta in modo diverso quando si desidera il fattore di zoom.

Ad esempio, se lasci che il tuo intervallo scada da 0 a 100, tratteresti 0-50 in un modo e 50-100 in un altro modo (rispettivamente 10-100% e 100-500%).

Per ottenere il comportamento dello snap, sono abbastanza sicuro che puoi semplicemente sostituire setValue () per scattare l'intervallo desiderato. Quindi, usando 0-100 come intervallo di valori, se setValue () viene chiamato con 47-53, basta agganciare il valore a 50.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top