JScrollBars 值变化不保持最大值不变

如何解决JScrollBars 值变化不保持最大值不变

所以我有 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,SpringLayout.HORIZONTAL_CENTER,base);
              layout.putConstraint(SpringLayout.VERTICAL_CENTER,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
            
        }
}

我将 JScrollPane 滚动条设置为具有如上所示的新最大值,但我将在此处再次输入。 (这样做是为了我可以在相同的视图大小下滚动得更慢(我的单位增量 1 对我来说仍然滚动得太快))

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

所以我启动了程序,jscrollbars 的最大大小肯定增加了,这就是我想要的。有了这个,我还有以下代码来使用 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;

问题:

当我按下这些键中的任何一个时,滚动条的最大值不再是我设置的,而是将它们的最大尺寸认为 map 的首选尺寸(我通过打印出最大尺寸对此进行了测试在我按下任何键之前和之后的滚动条,它们发生了变化)。我相信这个问题源于 bar.setValue() 方法,不知道为什么。

那么,如何防止 jscrollbars 改变它们的最大尺寸?我的目标是使用 W、A、S 和 D 键滚动整个 jlabel map,并将最大值设置为 jscrollbars。

解决方法

JScrollBar 使用 DefaultBoundedRangeModel 来跟踪值。

BasicScrollPaneUI 内部是调用 syncScrollPaneWithViewport() 的方法 hsb.setValues(...)

setValues(...) 方法调用 BoundedRangeModel 的 setRangeProperties(...) 方法。

不幸的是,最大值是根据添加到视口的面板宽度重置的。

所以我的建议是您需要创建自己的自定义 BoundedRangeModel 并确保“max”值没有被重置(到一个较小的值)?不知道会不会影响滚动。

您可以在以下位置查看 DefaultBoundedRangeModel 和 BasicScrollPaneUI 的源代码:

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

这是我用来验证最大值是否被 setValue(...) 语句本身外部的方法更改的测试代码。然而,这个语句显然会导致视口的同步发生:

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,SpringLayout.HORIZONTAL_CENTER,base);
              layout.putConstraint(SpringLayout.VERTICAL_CENTER,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);
            }

        }
}

编辑:

以上代码演示了如何扩展 DefaultBoundedRangeModel。目前,它只显示每个方法被调用的时间,以尝试了解模型更新时的逻辑流程。

请注意,在“setMaximum”之后,会使用正确的最大值调用“setRangeProperties”。

但是,第二次调用了“setRangeProperties”(这是我在答案顶部尝试解释的 BasicScrollPaneUI 中发现的逻辑的结果)。只是这次调用时以面板宽度(1200)为最大值,而不是你原来指定的8000。

因此看起来您需要重写 setRangeProperties(...) 方法的代码以始终使用 8000 作为最大值。因此,也许当您创建 MyBoundedRangeModel 的实例时,您会传入您想要使用的最大值。

我给了您一个链接,您可以在其中找到 DefaultBoundedRangeModel 的源代码。首先从 setRangeProperties(...) 方法复制代码以用作起点。然后您需要自定义代码以确保最大值始终为 8000。

这是我对如何解决问题的最佳猜测。不知道行不行。

,

我终于让你的例子在某种程度上起作用了。我可以使用 WASD 键移动滚动条。

我从 JScrollPane 类字段中删除了静态。

我通过调用 SwingUtilities invokeLater 方法启动了 Swing 应用程序。此方法可确保在 Event Dispatch Thread 上创建和执行 Swing 组件。

我将键侦听器添加到 JScrollPane,而不是 JFrame

我将 JScrollPane 设为可聚焦,因此按键实际上会记录。 Key bindings 会轻松很多。

这是完整的可运行代码。

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,base);
       layout.putConstraint(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) {
    }
 
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?