[英]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.