简体   繁体   English

JScrollBars 值更改不保持最大值不变

[英]JScrollBars value change not keeping maximum value constant

So I have the JScrollPane view like so (this is my minimal reproducible example),所以我有这样的 JScrollPane view (这是我最小的可重现示例),

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

I set the JScrollPane scroll bars to have new maximum values which can be seen above, but I will type here again.我将 JScrollPane 滚动条设置为具有新的最大值,可以在上面看到,但我会在这里再次输入。 (This is done so I can scroll slower in the same view size (My unit increment of 1 was still scrolling too fast for me)) (这样做是为了让我可以在相同的视图大小下滚动得更慢(我的单位增量 1 对我来说仍然滚动得太快))

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

So I start up the program and the jscrollbars have defintely increased in their maximum size, which is what I want.所以我启动了程序,jscrollbars 的最大尺寸肯定增加了,这就是我想要的。 With that, I also have the following code to change the jscrollbars values using a keylistener, which can also be seen above.有了这个,我还有以下代码来使用 keylistener 更改 jscrollbars 值,这也可以在上面看到。

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;

Problem:问题:

When I press any of those keys, the scrollbars maximums are no longer what I set them too, and instead have their maximum sizes defaulted to the preferred size of map (I tested this by printing out the maximum sizes of the scrollbars before and after I pressed any of the keys and they changed).当我按下这些键中的任何一个时,滚动条的最大值也不再是我设置的值,而是将它们的最大尺寸默认为map的首选尺寸(我通过在我之前和之后打印出滚动条的最大尺寸来测试这一点按下任何一个键,它们就改变了)。 I believe this problem derives from the bar.setValue() method, not sure why.我相信这个问题源于bar.setValue()方法,不知道为什么。

So, how can I prevent the jscrollbars from changing their maximum sizes?那么,如何防止 jscrollbars 改变它们的最大尺寸呢? My goal is to scroll the entirety of the jlabel map with solely W, A, S, and D keys with my set maximum values to the jscrollbars.我的目标是滚动整个 jlabel map ,仅使用 W、A、S 和 D 键以及我设置的最大值到 jscrollbars。

The JScrollBar uses the DefaultBoundedRangeModel to track the values. JScrollBar使用DefaultBoundedRangeModel来跟踪值。

Inside the BasicScrollPaneUI is the method syncScrollPaneWithViewport() which invokes hsb.setValues(...) . BasicScrollPaneUI内部是调用hsb.setValues(...)的方法syncScrollPaneWithViewport() ) 。

The setValues(...) method invokes the setRangeProperties(...) method of the BoundedRangeModel. setValues(...) 方法调用 BoundedRangeModel 的setRangeProperties(...)方法。

Unfortunately the max value is reset based on the width of the panel added to the viewport.不幸的是,最大值是根据添加到视口的面板的宽度重置的。

So my suggestion is that you will need to create your own custom BoundedRangeModel and make sure the "max" value is not reset (to a smaller value)?所以我的建议是您需要创建自己的自定义 BoundedRangeModel 并确保“最大值”不重置(设置为更小的值)? I don't know if this will affect scrolling or not.我不知道这是否会影响滚动。

Your can check out the source code for the DefaultBoundedRangeModel and BasicScrollPaneUI at:您可以在以下位置查看 DefaultBoundedRangeModel 和 BasicScrollPaneUI 的源代码:

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

Here is the test code I used to verify that the max value was being changed by a method external to the setValue(...) statement itself.这是我用来验证最大值是否被 setValue(...) 语句本身之外的方法更改的测试代码。 However, this statement does apparently cause the synching of the viewport to happen:然而,这个语句显然会导致视口的同步发生:

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

        }
}

Edit:编辑:

Above code demonstrates how to extend the DefaultBoundedRangeModel.上面的代码演示了如何扩展 DefaultBoundedRangeModel。 Currently, it only shows when each method is invoked to try to understand the logic flow when the model is updated.目前,它仅显示何时调用每个方法以尝试了解更新 model 时的逻辑流程。

Note that after the "setMaximum" the "setRangeProperties" is invoked with the correct max value.请注意,在“setMaximum”之后,会使用正确的最大值调用“setRangeProperties”。

However, "setRangeProperties" is invoked a second time (as a result of logic found in the BasicScrollPaneUI as I tried to explain at the top of my answer).但是,“setRangeProperties”被第二次调用(由于我试图在答案顶部解释的 BasicScrollPaneUI 中的逻辑结果)。 Only this time it is invoked with the width of the panel (1200) as the maximum value, not the 8000 you originally specified.只是这次它以面板的宽度 (1200) 作为最大值调用,而不是您最初指定的 8000。

So it would appear you need to rewrite the code of the setRangeProperties(...) method to always use the 8000 as the maximum value.因此,您似乎需要重写setRangeProperties(...)方法的代码以始终使用 8000 作为最大值。 So maybe when you create an instance of the MyBoundedRangeModel you pass in the maximum value you want to use.因此,也许当您创建MyBoundedRangeModel的实例时,您会传入要使用的最大值。

I gave you a link where you can find the source code for the DefaultBoundedRangeModel.我给了你一个链接,你可以在其中找到 DefaultBoundedRangeModel 的源代码。 Start by copying the code from the setRangeProperties(...) method to use as a starting point.首先从setRangeProperties(...)方法复制代码以用作起点。 Then you need to customize the code to make sure the maximum is always 8000.然后您需要自定义代码以确保最大值始终为 8000。

This is my best guess as to how you can solve the problem.这是我对如何解决问题的最佳猜测。 I don't know if it will work or not.我不知道它是否会起作用。

I finally got your example to work somewhat.我终于让你的例子有点工作了。 I can move the scroll bars with the WASD keys.我可以用 WASD 键移动滚动条。

I removed the static from the JScrollPane class field.我从JScrollPane class 字段中删除了 static 。

I started the Swing application with a call to the SwingUtilities invokeLater method.我通过调用SwingUtilities invokeLater方法启动了 Swing 应用程序。 This method ensures that the Swing components are created and executed on the Event Dispatch Thread .此方法确保在Event Dispatch Thread上创建和执行 Swing 组件。

I added the key listener to the JScrollPane , rather than the JFrame .我将关键侦听器添加到JScrollPane ,而不是JFrame

I made the JScrollPane focusable, so the key presses would actually register.我使JScrollPane可聚焦,因此按键实际上会注册。 Key bindings would work a lot easier. 键绑定会更容易工作。

Here's the complete runnable code.这是完整的可运行代码。

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) {
    }
 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM