java: el cambio de valor de JScrollBars no mantiene constante el valor máximo

CorePress2024-01-24  9

Así que tengo la vista JScrollPane así (este es mi ejemplo mínimo reproducible),

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class Test implements KeyListener {
           private static JScrollPane view;   

           public Test() { 
               create();
           }

           public static void main(String[] args) {
               new Test();
           }

           public void create() {
              JFrame frame = new JFrame(); //make frame
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setSize(1000, 1000);
              frame.setLocationRelativeTo(null);
              frame.setResizable(false);

              SpringLayout layout = new SpringLayout(); 
              JPanel base = new JPanel();
              base.setPreferredSize(new Dimension(1000, 1000));
              base.setLayout(layout);
                
              JPanel map = new JPanel();  //make panel for the scrollpane
              map.setPreferredSize(new Dimension(1000, 1000));
              
              
              view = new JScrollPane(map);  //initialize scrollpane and add to base with a SpringLayout
              view.setPreferredSize(new Dimension(300, 300));
              layout.putConstraint(SpringLayout.HORIZONTAL_CENTER, view, 0, SpringLayout.HORIZONTAL_CENTER, base);
              layout.putConstraint(SpringLayout.VERTICAL_CENTER, view, 0,  SpringLayout.VERTICAL_CENTER, base);
              base.add(view); //add scrollpane to base jpanel

              frame.add(base);
              frame.setVisible(true);
              frame.addKeyListener(this);
              
              view.getHorizontalScrollBar().setMaximum(10*800); //set wanted max sizes of each scroll bar
              view.getVerticalScrollBar().setMaximum(10*800);
           }

           public void keyPressed(KeyEvent event) {  //W, A, S, D keys to change values of jscrollbars
               switch(event.getKeyCode()) {
              case KeyEvent.VK_W:
                   view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() - 10*0.9));
                   break;
              case KeyEvent.VK_S:
                   view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() + 10*0.9));
                   break;
              case KeyEvent.VK_A:
                   view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() - 10*0.9));
                   break;
              case KeyEvent.VK_D:
                   view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() + 10*0.9));
                   break;
               }
           }

        @Override
        public void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub
            
        }
}

Configuré las barras de desplazamiento de JScrollPane para que tengan nuevos valores máximos que se pueden ver arriba, pero escribiré aquí nuevamente. (Esto se hace para poder desplazarme más lentamente en el mismo tamaño de vista (mi incremento de unidad de 1 todavía se desplazaba demasiado rápido para mí))

view.getHorizontalScrollBar().setMaximum(10*800);
view.getVerticalScrollBar().setMaximum(10*800);

Así que inicio el programa y las jscrollbars definitivamente han aumentado en su tamaño máximo, que es lo que quiero. Con eso, también tengo el siguiente código para cambiar los valores de jscrollbars usando un keylistener, que también se puede ver arriba.

case KeyEvent.VK_W:
    view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() - 10*0.3));
    break;
case KeyEvent.VK_S:
    view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() + 10*0.3));
    break;
case KeyEvent.VK_A:
    view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() - 10*0.3));
    break;
case KeyEvent.VK_D:
    view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() + 10*0.3));
    break;

Problema:

Cuando presiono cualquiera de esas teclas, los máximos de las barras de desplazamiento ya no son los que configuré y, en su lugar, sus tamaños máximos están predeterminados al tamaño preferido.del mapa (probé esto imprimiendo los tamaños máximos de las barras de desplazamiento antes y después de presionar cualquiera de las teclas y cambiaron). Creo que este problema se deriva del método bar.setValue(), no estoy seguro de por qué.

Entonces, ¿cómo puedo evitar que las jscrollbars cambien sus tamaños máximos? Mi objetivo es desplazar la totalidad del mapa jlabel con únicamente las teclas W, A, S y D con mis valores máximos establecidos para las jscrollbars.

Una vez más tenemos otra pregunta sin un ejemplo mínimo reproducible. Si la gente no ha leído tus últimos 3 o 4pregunta, no tienen idea de lo que estás intentando hacer.

-camickr

26/03/2021 a las 23:16

@camickr Lo siento, déjame hacer eso ahora mismo

- LuckyBandit74

26/03/2021 a las 23:18

@camickr Muy bien, en la parte superior dije que el código es mi ejemplo mínimo reproducible. He incluido todo para la clase principal y debajo también tengo el código parar la clase Mapa. Creo que eso es suficiente.

- LuckyBandit74

26/03/2021 a las 23:27

El código publicado no se compila. Por lo tanto no es un ejemplo mínimo reproducible. Deberíamos poder copiar/pegar/compilar y probar para ver el comportamiento descrito. Además, no necesitas la clase Map. Puedes simplemente crear un JPanel y establecer el tamaño preferido. Probé esto imprimiendo los tamaños máximos de las barras de desplazamiento antes y después de presionar cualquiera de las teclas; esto debería ser parte del MRE para que podamos ver exactamente cómo lo probaste.

& ndash;camickr

27 de marzo de 2021 a las 3:24

@camickr Muy bien, edité mi publicación, edité el código y lo probé yo mismo; reproduce mi problema. Perdón por ser tan ignorante aquí y gracias por tener paciencia conmigo. Pondré más esfuerzo en futuras publicaciones.

- LuckyBandit74

27 de marzo de 2021 a las 5:22



------------------------------------

JScrollBar utiliza DefaultBoundedRangeModelpara realizar un seguimiento de los valores.

Dentro de BasicScrollPaneUI está el método syncScrollPaneWithViewport() que invoca hsb.setValues(...).

El método setValues(...) invoca el método setRangeProperties(...) de BoundedRangeModel.

Desafortunadamente, el valor máximo se restablece según el ancho del panel agregado a la ventana gráfica.

Así que mi sugerencia es que necesitarás crear tu propio BoundedRangeModel personalizado y asegurarte de que el parámetro "max" ¿El valor no se restablece (a un valor menor)? No sé si esto afectará el desplazamiento o no.

Puedes consultar el código fuente de DefaultBoundedRangeModel y BasicScrollPaneUI en:

https://github.com/openjdk/jdk/tree/master/src/java.desktop/share/classes/javax

Aquí está el código de prueba que utilicé para verificar que el v máximoEl valor estaba siendo cambiado por un método externo a la propia declaración setValue(...). Sin embargo, esta declaración aparentemente causa que se produzca la sincronización de la ventana gráfica:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class Test implements KeyListener {
           private static JScrollPane view;

           public Test() {
               create();
           }

           public static void main(String[] args) {
                SwingUtilities.invokeLater( () -> new Test() );
           }

           public void create() {
              JFrame frame = new JFrame(); //make frame
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              frame.setSize(1000, 1000);
              frame.setLocationRelativeTo(null);
              frame.setResizable(false);

              SpringLayout layout = new SpringLayout();
              JPanel base = new JPanel();
              base.setPreferredSize(new Dimension(1000, 1000));
              base.setLayout(layout);

              JPanel map = new JPanel();  //make panel for the scrollpane
              map.setPreferredSize(new Dimension(1200, 1200));


              view = new JScrollPane(map);  //initialize scrollpane and add to base with a SpringLayout
              view.setPreferredSize(new Dimension(300, 300));
              layout.putConstraint(SpringLayout.HORIZONTAL_CENTER, view, 0, SpringLayout.HORIZONTAL_CENTER, base);
              layout.putConstraint(SpringLayout.VERTICAL_CENTER, view, 0,  SpringLayout.VERTICAL_CENTER, base);
              base.add(view); //add scrollpane to base jpanel

                view.getHorizontalScrollBar().setModel( new MyBoundedRangeModel() );
                System.out.println("default value");
                System.out.println(view.getHorizontalScrollBar().getMaximum());


              frame.add(base);
              frame.setVisible(true);
              frame.addKeyListener(this);

                System.out.println("after Visible");
                System.out.println(view.getHorizontalScrollBar().getMaximum());


               view.getHorizontalScrollBar().setMaximum(10*800); //set wanted max sizes of each scroll bar
               view.getVerticalScrollBar().setMaximum(10*800);

                System.out.println("after changing maximum");
                System.out.println(view.getHorizontalScrollBar().getMaximum());


           }

           public void keyPressed(KeyEvent event)
           {
                System.out.println("Before");
                System.out.println(view.getHorizontalScrollBar().getValue());
                System.out.println(view.getHorizontalScrollBar().getMaximum());

               switch(event.getKeyCode()) {
              case KeyEvent.VK_W:
                   view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() - 10*0.9));
                   break;
              case KeyEvent.VK_S:
                   view.getVerticalScrollBar().setValue((int) (view.getVerticalScrollBar().getValue() + 10*0.9));
                   break;
              case KeyEvent.VK_A:
                   view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() - 10*0.9));
                   break;
              case KeyEvent.VK_D:
                   view.getHorizontalScrollBar().setValue((int) (view.getHorizontalScrollBar().getValue() + 10*0.9));
                   break;
               }

                System.out.println("after");
                System.out.println(view.getHorizontalScrollBar().getValue());
                System.out.println(view.getHorizontalScrollBar().getMaximum());
           }

        @Override
        public void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }

        class MyBoundedRangeModel extends DefaultBoundedRangeModel
        {
            @Override
            public void setValue(int i)
            {
                System.out.println("setValue");
                super.setValue(i);
            }

            @Override
            public void setMaximum(int i)
            {
                System.out.println("setMaximum");
                super.setMaximum(i);
            }
            @Override
            public void setMinimum(int i)
            {
                System.out.println("setMinimum");
                super.setMinimum(i);
            }

            @Override
            public void setExtent(int i)
            {
                System.out.println("setExtent");
                super.setExtent(i);
            }

            @Override
            public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting)
            {
                System.out.println("setRangeProperties");
                System.out.println(newValue + " : " + newExtent + " : " + newMax);
                super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
            }

        }
}

Editar:

El código anterior demuestra cómo extender DefaultBoundedRangeModel. Actualmente, solo muestra cuándo se invoca cada método para intentar comprender el flujo lógico cuando se actualiza el modelo.

Tenga en cuenta que después de la opción "setMaximum" el comando "setRangeProperties" se invoca con el valor máximo correcto.

Sin embargo, "setRangeProperties" se invoca por segunda vez (como resultado de la lógica encontrada en BasicScrollPaneUI como intenté explicar en la parte superior de mi respuesta). Solo que esta vez se invoca con el ancho del panel (1200) como valor máximo, no los 8000 que especificó originalmente.d.

Entonces parece que necesitas reescribir el código del método setRangeProperties(...) para usar siempre el 8000 como valor máximo. Entonces, tal vez cuando crees una instancia de MyBoundedRangeModel pases el valor máximo que deseas usar.

Te di un enlace donde puedes encontrar el código fuente de DefaultBoundedRangeModel. Comience copiando el código del método setRangeProperties(...) para usarlo como punto de partida. Luego necesitas personalizar el código para asegurarte de que el máximo sea siempre 8000.

Esta es mi mejor suposición sobre cómo resolver el problema. No sé si funcionará o no.

3

Estoy listo para implementar esto pero, sinceramente, no soy tan bueno como tú. Miré el código fuente y vi que BoundedRangeModel es una interfaz. Soy un programador bastante novato, ¿podría proporcionarme solo los conceptos básicos para crear mi propio BoundedRangeModel personalizado? No necesito todo, sólo lo suficiente para empezar. Perdón por ser un matiz.

- LuckyBandit74

27/03/2021 a las 15:56

@LuckyBandit74 Lo siento, pegué el código incorrecto en mi respuesta. Mi código de prueba ha sido agregado. Consulte la edición para obtener una explicación de cómo probé.

-camickr

27/03/2021 a las 17:48

¡Gracias por el código! Probaré esto más tarde ya que actualmente no estoy frente a mi computadora, pero verificaré tu respuesta cuando haya jugueteado con ella

- LuckyBandit74

27/03/2021 a las 17:57



------------------------------------

Finalmente entendí tu ejemploe trabajar un poco. Puedo mover las barras de desplazamiento con las teclas WASD.

Eliminé la estática del campo de clase JScrollPane.

Inicié la aplicación Swing con una llamada al método invokeLater de SwingUtilities. Este método garantiza que los componentes Swing se creen y ejecuten en el hilo de envío de eventos.

Agregué el detector de claves a JScrollPane, en lugar de JFrame.

Hice que JScrollPane fuera enfocable, de modo que las pulsaciones de teclas realmente se registraran. Las combinaciones de teclas funcionarían mucho más fácilmente.

Aquí está el código ejecutable completo.

import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;

public class JScrollPaneTest implements KeyListener {
    private JScrollPane view;   

    public JScrollPaneTest() { 
        create();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                 new JScrollPaneTest();
            }
        });
    }

    public void create() {
       JFrame frame = new JFrame(); //make frame
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.setSize(1000, 1000);
       frame.setLocationRelativeTo(null);
       frame.setResizable(false);

       SpringLayout layout = new SpringLayout(); 
       JPanel base = new JPanel();
       base.setPreferredSize(new Dimension(1000, 1000));
       base.setLayout(layout);
         
       JPanel map = new JPanel();  //make panel for the scrollpane
       map.setPreferredSize(new Dimension(1000, 1000));
       
       
       view = new JScrollPane(map);  //initialize scrollpane and add to base with a SpringLayout
       view.setFocusable(true);
       view.setPreferredSize(new Dimension(300, 300));
       layout.putConstraint(SpringLayout.HORIZONTAL_CENTER, view, 0, SpringLayout.HORIZONTAL_CENTER, base);
       layout.putConstraint(SpringLayout.VERTICAL_CENTER, view, 0,  SpringLayout.VERTICAL_CENTER, base);
       base.add(view); //add scrollpane to base jpanel

       frame.add(base);
       frame.setVisible(true);
       view.addKeyListener(this);
       
       view.getHorizontalScrollBar().setMaximum(10*800); //set wanted max sizes of each scroll bar
       view.getVerticalScrollBar().setMaximum(10*800);
    }

    public void keyPressed(KeyEvent event) { // W, A, S, D keys to change values of jscrollbars
        int verticalValue = view.getVerticalScrollBar().getValue();
        int horizontalValue = view.getHorizontalScrollBar().getValue();
        
        switch (event.getKeyCode()) {
        case KeyEvent.VK_W:
            view.getVerticalScrollBar().setValue(verticalValue - 10);
            break;
        case KeyEvent.VK_S:
            view.getVerticalScrollBar().setValue(verticalValue + 10);
            break;
        case KeyEvent.VK_A:
            view.getHorizontalScrollBar().setValue(horizontalValue - 10);
            break;
        case KeyEvent.VK_D:
            view.getHorizontalScrollBar().setValue(horizontalValue + 10);
            break;
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
 
}

2

@camickr Tienes toda la razón. Dormí durante la noche e inmediatamente me desperté para probarlo, olvidando el hecho de que mis máximos debían permanecer constantes y apagar mi computadora antes de darme cuenta. Acabo de regresar aquí para comentar y también para ver tu comentario. Además, ¿ha tenido suerte si ya intentó resolver este problema anteriormente?

- LuckyBandit74

27/03/2021 a las 15:34

@Gilbert Le Blanc Así que mi ejemplo que publiqué ya debería haberse movido con las teclas W, A, S y D. El problema era que la va máximaLos valores que configuré para las barras de desplazamiento cambiaron sus valores máximos al presionar esas teclas. En el ejemplo que publicó, mis valores máximos establecidos se ignoraron y tomaron los tamaños de barra de desplazamiento predeterminados. Aunque no es exactamente lo que quería, te agradezco que lo intentes y te tomes el tiempo de responder.

- LuckyBandit74

27/03/2021 a las 15:41

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare