So I have the JScrollPane view
like so (this is my minimal reproducible example),
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. (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))
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. With that, I also have the following code to change the jscrollbars values using a keylistener, which can also be seen above.
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). I believe this problem derives from the bar.setValue()
method, not sure why.
So, how can I prevent the jscrollbars from changing their maximum sizes? 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.
The JScrollBar
uses the DefaultBoundedRangeModel
to track the values.
Inside the BasicScrollPaneUI
is the method syncScrollPaneWithViewport()
which invokes hsb.setValues(...)
.
The setValues(...) method invokes the setRangeProperties(...)
method of the BoundedRangeModel.
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)? I don't know if this will affect scrolling or not.
Your can check out the source code for the DefaultBoundedRangeModel and BasicScrollPaneUI at:
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. 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. Currently, it only shows when each method is invoked to try to understand the logic flow when the model is updated.
Note that after the "setMaximum" the "setRangeProperties" is invoked with the correct max value.
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). Only this time it is invoked with the width of the panel (1200) as the maximum value, not the 8000 you originally specified.
So it would appear you need to rewrite the code of the setRangeProperties(...)
method to always use the 8000 as the maximum value. So maybe when you create an instance of the MyBoundedRangeModel
you pass in the maximum value you want to use.
I gave you a link where you can find the source code for the DefaultBoundedRangeModel. Start by copying the code from the setRangeProperties(...)
method to use as a starting point. Then you need to customize the code to make sure the maximum is always 8000.
This is my best guess as to how you can solve the problem. I don't know if it will work or not.