rkcole.com
Two JScrollPanes can be synchronized so that both panes will respond to the action of a single JScrollBar. The trick is to synchronize two JScrollBar data models. This example should be adaptable to JSliders and JProgressBars.
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
/**
* Simple panel that illustrates the synchronization of two JScrollPanes.
* The number of elements in the two JList components do not have to be
* the same. The ChangeListener will synchronize their respective
* BoundedRangeModels and scale as necessary.
* <p> Note: Since it is the right JScrollBar's model-ChangeListener
* that listens for changes from the left JScrollBar, it is the right JList
* that controls the total number of elements shown in the JScrollPanes. </p>
*
* @author R. Kevin Cole
*/
public class ScrollBarSynchronizer extends JPanel
{
/**
* Two JLists are created and initialized from Vectors and
* then added to JScrollPanes to test the synchronized JScrollBars.
*/
public ScrollBarSynchronizer()
{
super( new BorderLayout() );
// create something to scroll
Vector<String> leftData = new Vector<String>();
Vector<String> rightData = new Vector<String>();
for( int i = 0; i < 71; i++ )
{
rightData.add( "Right " + i );
// the modulo operator is used to create a different number
// of elements within each scrolling region.
if( (i % 2) == 0 )
leftData.add( "Left " + i );
}
JScrollPane leftScroller = new JScrollPane( new JList( leftData ) );
leftScroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
JScrollPane rightScroller = new JScrollPane( new JList( rightData ) );
JScrollBar leftBar = leftScroller.getVerticalScrollBar();
JScrollBar rightBar = rightScroller.getVerticalScrollBar();
MyChangeListener listener = new MyChangeListener(leftBar.getModel());
rightBar.getModel().addChangeListener( listener );
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT
, leftScroller
, rightScroller );
add( split, BorderLayout.CENTER );
}
/**
* Test routine.
* @param arg ignored.
*/
public static void main( String[] arg )
{
JFrame frame = new JFrame("Test ScrollBarSynchronizer");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane( new ScrollBarSynchronizer() );
frame.setSize( 640, 480 );
frame.setLocation( 100, 100 );
frame.setVisible(true);
}
}/**
* Synchronize the data models of any two JComponents that use a
* BoundedRangeModel ( such as a JScrollBar, JSlider or ProgressBar).
*
* @see javax.swing.BoundedRangeModel
* @see javax.swing.event.ChangeListener
* @Author R. Kevin Cole
*/
class MyChangeListener implements ChangeListener
{
private BoundedRangeModel myModel;
/**
* @param model This model is forced to move in synchronization
* to this ChangeListener's event-source.
*/
public MyChangeListener( BoundedRangeModel model )
{
myModel = model;
}// - begin implementation of ChangeListener
//
/** Envoked when the target of the listener has changed its state.
*/
public void stateChanged( ChangeEvent e )
{
BoundedRangeModel sourceModel = (BoundedRangeModel) e.getSource();
int sourceDiff = sourceModel.getMaximum() - sourceModel.getMinimum();
int destDiff = myModel.getMaximum() - myModel.getMinimum();
int destValue = sourceModel.getValue();
if( sourceDiff != destDiff )
destValue = (destDiff * sourceModel.getValue())/sourceDiff;
myModel.setValue( myModel.getMinimum() + destValue );
}
//
// - end implementation of ChangeListener
}