Pergunta

Estou tentando imitar (ou encontrar um componente preexistente) que imite o controle deslizante do Zoom do Word 2007:

Two state zoom bar

Existem duas diferenças principais entre este componente e um Java JSLider padrão:

  1. Não se encaixa nos carrapatos, exceto em 100%, e se encaixa enquanto você está deslizando a barra, e não quando solta o mouse
  2. O controle deslizante não é linear o caminho todo: a metade esquerda do controle deslizante passa de 10% a 100%; O lado direito passa de 100% a 500%.

Aqui está o que tenho até agora: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() + "%");
    }

}

Basicamente, apenas imitando o visual, mas sem esses dois recursos mencionados acima. Eu não vejo nada no JSLider API Isso me permite obter esse comportamento. Eu teria que começar do zero para obter esse comportamento? Nesse caso, não vale o meu tempo, mas se alguém souber de uma boa maneira de obter esse comportamento, acho que seria bom ter no projeto em que estou trabalhando.

Obrigado,

usuario


EDITAR:

Agradecemos ao PSPEED pela idéia sobre apenas mapear os 0..50 e 50..100 para valores diferentes. O código a fazer isso está abaixo.

Infelizmente, a idéia de mudar o SetValue para Snap não funcionou.

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: Outra diferença que noto é que um JButton não permite que você o segure até o botão de fogo várias vezes, enquanto os botões +/- no escritório fazem. Alguma ideia de como imitar isso?

Foi útil?

Solução

Eu acredito que você pode obter todo esse comportamento com um modelo de limpeza limite personalizado. A chave é tornar o modelo relatar um tipo normal de alcance de valores, mas tratá -lo de maneira diferente quando você deseja o seu fator de zoom.

Por exemplo, se você deixar seu intervalo funcionar de 0 a 100, trataria 0-50 de uma maneira e 50-100 de outra maneira (10-100% e 100-500%, respectivamente).

Para obter o comportamento do SNAP, tenho certeza de que você pode substituir o setValue () para eliminar o intervalo que deseja. Portanto, usando 0-100 como intervalo de valor, se setValue () for chamado com 47-53, basta tirar o valor para 50.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top