Monday, September 5, 2011

Customize the Look, Feel and Content of a JList Component



Today's tutorial is mercifully simple. We're going to customize the look and feel of the JList component, using what's refered to as a Cell Renderer.


Recommended Experience Level (1:new - 10:expert): 3
Pre-requisites: Simple Image I/O, Swing forms


In this tutorial, we will execute the following steps:
  1. Create a customized data type (which we will use to populate our list).
  2. Create a customized Cell Renderer (by implementing ListCellRenderer).
  3. Display our new list

Create a customized data type.


Our customized data type is, for our example, simply a name and an image wrapped into a data type. One of the many simple beauties about the JList object, is that it can literally take any type of object as its list contents. This could be map tiles, beach balls, shoes, whatever. If the object contains relevant data to display to the user on a list - the JList can handle it. We proceed with our simple construct:


import java.awt.Image;
import javax.swing.Icon;
import javax.swing.ImageIcon;

public class CustomListItem {

// The image representing this item
Image image;

// The name of this item
String name;

/**
* Set Image for this item
*
* @param image
* The image representing this item
*/

public void setImage(Image image) {
this.image = image;
}

/**
* Get the Icon for this item.
*
* @return
* An iconized form of image.
*/

public Icon getIcon() {

ImageIcon icon = new ImageIcon(image.getScaledInstance(16,16,
Image.SCALE_FAST));
return icon;
}

/**
* Set the name of this item
*
* @param name
* The new name of this item
*/

public void setName(String name) {
this.name = name;
}

/**
* Get the name of this item
*
* @return
* The name of this item
*/

public String getName() {
return name;
}
}


With our sample class complete, we are able to move on to creation of our customized cell renderer.


Create a customized cell renderer


Before we start, let's take a look at the naming of the cell renderer. When we talk about a JList's 'cell' we are talking about a selectable unit on the list. The JList display list is comprised entirely of cells, each cell representing an object that belongs to the JList.



When we create our customized cell renderer, we are literally building the routine used by the JList to paint its cells. It will call the public Component getListCellRendererComponent() method for each object in its list, and then place the Component object we return into the JList's UI.


In this example, we're returning a trumped-up JLabel (JLabel is convenient because it's small and rectangular by nature, and also has a good method for managing co-existing icons and text easily) to the JList as our display component.



/**
* A cell renderer, which takes list items (as objects) and
* translates them into list selection displays. Used
* automatically by JList
*/

static class CustomCellRenderer
implements ListCellRenderer {

@Override
/**
* When the CustomCellRenderer is loaded into our JList,
* the list will call this method automatically to get the
* Components which are used for selection on the list.
*/

public Component getListCellRendererComponent(
JList list, // The list which is calling this method
Object value, // The object which represents the value/
int index, // The index # of this object within the list
boolean isSelected, // Indicates that this item is currently
// selected.

boolean cellHasFocus // Indicates whether the cell currently
// has the focus

) {

// Cast our 'value' object into an object of our known class
CustomListItem item = (CustomListItem) value;

// Create a new JLabel (we'll return this to the list)
JLabel cell = new JLabel();

// Set the preferred size for each display 'label'
cell.setPreferredSize(new Dimension(100,20));

// Set the text portion of the cell, according to the value
// object's .toString() method.

cell.setText(item.getName());

// Set the image portion of the cell, acquiring it from the
// value object.

cell.setIcon(item.getIcon());

// Do dome styling to the label.
cell.setIconTextGap(10); // places 10 px between image & text
cell.setFont(new Font(
"Times New Roman",
Font.PLAIN,
12)); // Set ourselves a new font for the cells

// If the current cell is selected
if (isSelected) {
// Set a blue border and backdrop
cell.setBorder(new LineBorder(Color.blue));
cell.setBackground(new Color(220,220,235));
}
else
{
// Set the deault background
cell.setBorder(new LineBorder(list.getBackground()));
cell.setBackground(list.getBackground());
}

// Turn opacity on so you can see our background changes
cell.setOpaque(true);

// Return the cell to the list for display
return cell;
}
}

As you can see, it's is extremely simple to implement the ListCellRenderer class, as it has only one method to override. Once inside of our method, all we had to do was create a new JLabel, assign it a few properties based on the object we passed in, configure it to our liking, then ship that puppy right back out. Very, very easy to do.


Of course we aren't quite finished yet. We have to load our new customized cell renderer into the JList, and we have to put it somewhere to see it.


Display our new list


In the following code segment, we'll instantiate all that we've created, set it up, and display it. Except for a few key lines, we're looking at your basic routine frame setup.


public static void main(String[] args) {

// Create 9 custom list items
// (there are more effective ways to set up items, this is
// drawn out so you can get an idea of exactly what goes into
// making our CustomListItem objects, and how exactly the JList
// can be loaded up via constructor.

CustomListItem item1 = new CustomListItem();
CustomListItem item2 = new CustomListItem();
CustomListItem item3 = new CustomListItem();
CustomListItem item4 = new CustomListItem();
CustomListItem item5 = new CustomListItem();
CustomListItem item6 = new CustomListItem();
CustomListItem item7 = new CustomListItem();
CustomListItem item8 = new CustomListItem();
CustomListItem item9 = new CustomListItem();

// NOTE: The loadImage routine is a custom routine I drew up
// for functionality. This tutorial does not explain how to
// utilize Image I/O in java. However, you can view the complete
// code in the downloadable for this tutorial.

item1.setImage(loadImage("./image/tile/DirtFloor.png"));
item1.setName("Dirt Floor");
item2.setImage(loadImage("./image/tile/Water.png"));
item2.setName("Water");
item3.setImage(loadImage("./image/tile/Grass.png"));
item3.setName("Grass");
item4.setImage(loadImage("./image/tile/DirtFloor.png"));
item4.setName("Dirt Floor 2");
item5.setImage(loadImage("./image/tile/Water.png"));
item5.setName("Water 2");
item6.setImage(loadImage("./image/tile/Grass.png"));
item6.setName("Grass 2");
item7.setImage(loadImage("./image/tile/DirtFloor.png"));
item7.setName("Dirt Floor 3");
item8.setImage(loadImage("./image/tile/Water.png"));
item8.setName("Water 3");
item9.setImage(loadImage("./image/tile/Grass.png"));
item9.setName("Grass 3");


// Wrap all of the CustomListItem objects into an array
CustomListItem[] items = {item1, item2, item3, item4, item5,
item6, item7, item8, item9};

// Create a new list, passing the array into the constructor
JList list = new JList(items);

// Assign our custom cell renderer to the JList object
list.setCellRenderer(new CustomCellRenderer());


// Place the list inside of a scroll pane, since the JList
// does not come with a scroll bar.

JScrollPane scrollPane = new JScrollPane(list);


// Setup a frame and place our list within it.
JFrame frame = new JFrame();

frame.setLayout(new FlowLayout());
frame.add(scrollPane);

frame.setSize(600,400);
frame.setTitle("Custom Style List Tutorial");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

After creating our custom objects, and processing them through our JList (and, subsequently, through our customized cell renderer), we are able to affect the look and feel of our list any way we like.




Happy Coding!


Timon


Download the tutorial files (note: you will probably have to do some editing and/or file movement to use the default images included in the download).


As always, feel free to peruse the Tutorial repository at http://code.google.com/p/tutorialsbytimon"

Friday, September 2, 2011

Using a MouseMotionListener to allow user interface with a grid.



In this tutorial, we will create a grid on a JPanel, and use the MouseMotionListener to make the panel aware of which grid space is currently highlighted by the user.  I came across this solution working on Project Hardin, in the process of developing the user interface for a grid-based map builder.

The principles used here can be applied to many formations other than grids, but you must be aware of where your interactive areas are, inside of your component, to use this method.  Obviously, there are 1,000 ways to do this.  Your use case might demand certain changes in coding and approach.


Overview


The following steps will be followed in this tutorial:

  1. Create the Listening Panel by extending JPanel.
  2. Create the properties that will allow us to draw tiles consistently, as well as keep track of the currently selected tile.
  3. Create / Overwrite the methods used to paint tiles on the panel.
  4. Add a MouseMotionListener to the panel, providing logic for the listener.
  5. Create public method to allow passing-in of MouseEvents.
  6. Create a Frame to display our work onto, then place our Listening Panel into a JScrollPane.
  7. Add a MouseMotionListener to the JScrollPane, sending MouseEvent signals to Listening Panel.
  8. Integrate, debug and Execute.


Create a Listening Panel




//This is easy! :)
static class ListenerDisplayPanel extends JPanel {

}


Create properties for Listening Panel



Next we have to set up our local properties for this class. These properties will be used like variable in the equations our drawing routine uses to draw tiles. We will also include a property that tracks the most recently pointed-ad tile.


static class ListenerDisplayPanel extends JPanel {

// Private member variables
private int mapWidth = 50; // Width of the map (in tiles)
private int mapHeight = 50; // Length of the map (in tiles)

// The width, in pixels, of a grid tile
private static final int TILE_WIDTH = 32;
private static final int TILE_HEIGHT = 32;

// The point (in grid format, not pixel) currently highlighted
private Point currentlyAt = new Point(0,0);

}


To insure that our panel is the correct size for its contents, we'll set the preferred size of the panel in our constructor, using our member variables.


public ListenerDisplayPanel() {

// Set the dimensions of the panel.
setPreferredSize(new Dimension(
mapWidth * TILE_WIDTH,
mapHeight * TILE_HEIGHT));
}

Already, our member variables are helping to define our panel.


Define grid-painting logic



At this point, we are ready to define how our panel should go about painting the grid and the grid highlighter. We haven't built the logic to update the currently selected tile yet, but we can use the currentlyAt property we crated in the last step in our equations.

We will add the logic to update the value of currentlyAt later in the tutorial.


/**
* Draws our graphics on the panel
*
* @param g
* The graphics component, which is something like the canvas
* you're painting on.
*/

protected void paintComponent(Graphics g) {

// Prevents images from getting gunky / forces full redraw
super.paintComponent(g);

// Initialize brush
g.setColor(Color.red);

// Draw a checkerboard on the screen based on height / weight value
for (int x = 0 ; x < mapWidth ; x++) {
for (int y = 0 ; y < mapHeight ; y++) {

if ( (0 == x % 2) && 0 == (y % 2) ||
(x % 2 > 0) && (y % 2 > 0 )) {

// draw a 32 x 32 rectangle
g.drawRect(x * TILE_WIDTH, y * TILE_HEIGHT,
TILE_WIDTH, TILE_HEIGHT);
}
else
{
g.fillRect(x * TILE_WIDTH, y * TILE_HEIGHT,
TILE_WIDTH, TILE_HEIGHT);
}
}
}

// Draw a green box around currently highligted square.
// Drawing two lines will thicken the line, making it
// more visible.

g.setColor(Color.green);
g.drawRect(
currentlyAt.x * TILE_WIDTH,
currentlyAt.y * TILE_HEIGHT,
TILE_WIDTH, TILE_HEIGHT);
g.drawRect(
(currentlyAt.x * TILE_WIDTH) + 1,
(currentlyAt.y * TILE_HEIGHT) + 1,
TILE_WIDTH - 2, TILE_HEIGHT - 2);
}

}



Note how our class-member variables, such as currentlyAt , TILE_WIDTH and TILE_HEIGHT are very present in this routine.


Add the MouseMotionListener to the Panel



There are two steps to this process. The first step is to create the MouseMotionListener, coding in logic for how the program should react to mouse movement over this panel. Because this panel wont be directly on the user interface (it will be inside of a JScrollPanel , we need to create a method to allow parenting containers to invoke our MouseMotionListener.

EventListeners can be intimidating to new programmers. If this is you, rest assured, with enough practice and practical application, they become much easier to use. In this tutorial, we're going to add our MouseMotionListener as an *anonymous* class, which is a fancy way of saying we'll make it up on the fly as we add it to our panel.

First, will code in our new MouseMotionListener as part of the ListenerDisplayPanel class. We'll also throw in a quick routine which will supply us with the *grid-based* position of the mouse, so we'll know which tile to highlight. That routine is defined after our we finish working on our constructor.


/**
* CONSTRUCTOR (Default)
*/

public ListenerDisplayPanel() {

// Set the dimensions of the panel.
setPreferredSize(new Dimension(
mapWidth * TILE_WIDTH,
mapHeight * TILE_HEIGHT));

// Add the mouse listener, which will process the mouse movement
// and record the current tile being highlighted

addMouseMotionListener(new MouseMotionListener() {

@Override
public void mouseDragged(MouseEvent e) {

// No code here.
}

@Override
public void mouseMoved(MouseEvent e) {

// Get the point where the mouse is located, in pixels,
// relative to this component.

Point relativePoint = e.getPoint();

// Translate the relativePoint into a point relative to
// the grid

Point gridPoint = mouseAtComponent_ToGrid(relativePoint);

// Assign this grid point to the currentlyAt variable,
// refreshing the currently selected tile space in
// memory.

currentlyAt = gridPoint;

// Refresh the invoking object and repaint the panel
Component source = (Component) e.getSource();
repaint();
source.validate();
}
});


setVisible(true);
}

/**
* Translates the position of the mouse point (in pixel units)
* into a grid position within a map.
*
* @param mouseAt
* The position of the mouse in Point format. You can get this
* from the e.getPoint() command of a MouseEvent object.
*
* @return
* The currently hovered-o'er grid.
*/

private Point mouseAtComponent_ToGrid(Point mouseAt) {

// divide the point by the tile size
return new Point(
(int) mouseAt.x / TILE_WIDTH,
(int) mouseAt.y / TILE_HEIGHT);
}

In brief, we made a very simple listener.

We simply had it report to us the position of the mouse (relative to the component - where the top left corner is (0,0)). Then we took that position, converted it with our mouseAtComponent_ToGrid() method, and updated the position of our currentlyAt property. Since this property is used by the paintComponent() method to draw a highlight around our selected square, all the dirty work is now done. We finished our listener logic by refreshing the object that made the call, as well as our panel
.


Exposing our Panel's MouseMotionListener



Finally, we need to give parent containers a way to pass MouseEvents to our panel so we can process them with our listener. A very simple way to do this is by creating a public method that takes a MouseEvent as an arguement and calls itself to process our event.


/**
* Allows calls to pass MouseEvents into this panel's
* MouseMotionListener.
*
* @param e
* The MouseEvent that was triggered by user action.
*/

public void mouseMoved(MouseEvent e) {
processMouseMotionEvent(e);
}


We have a little more work to do with listeners in this tutorial, but where the ListenerDisplayPanel is concerned, we're done with this section for now. In fact, this completes our construction of the


Create a Frame for display and Interaction



We'll begin, as always, by declaring our class and our class data members


/**
* We'll use this frame to show our display panel within a
* scroll panel.
*/

static class PanelDisplayFrame extends JFrame {

// Declare our listener panel and new Scroll pane as members.
ListenerDisplayPanel displayPanel = new ListenerDisplayPanel();
JScrollPane scrollPane;
}

Again, fairly simple stuff here.


Configuring the Frame



Next, we'll put some logic in the constructor to insert our completed listening panel ListenerDisplayPanel into a JScrollPane. Note that when we instantiate scrollPane, we add displayPanel as an argument to the JScrollPane constructor. This is the recommended way to add panels to your scrollpanes, as using other methods may get tricky.


/**
* CONSTRUCTOR (default)
*/

public PanelDisplayFrame() {

// Instantiate the scroll pane containing our display panel.
scrollPane = new JScrollPane(displayPanel);

// Update the scrollPane UI to prevent weird updating effects.
scrollPane.updateUI();

// Set up our scroll pane on the main panel.
setLayout(new BorderLayout());
add(scrollPane);
}



Add a listener to the frame



Finally, to get ourselves code-complete, we simply add another MouseMotionListener scrollPane. We're simply going to tell this listener to pass the MouseEvent signal down to the panel we created.

This completes the circuit, when we're done, we should be able to see our grid within a scrollable pane, and we should be able to see a highlight around the tile we are pointing to.


Since this code is also added to the constructor for the display frame, the new code will be bold-faced


public PanelDisplayFrame() {

// Instantiate the scroll pane containing our display panel.
scrollPane = new JScrollPane(displayPanel);

// Update the scrollPane UI to prevent weird updating effects.
scrollPane.updateUI();

// Set up our scroll pane on the main panel.
setLayout(new BorderLayout());
add(scrollPane);

// Add a mouse motion listener to pass messages to our
// display panel.

scrollPane.addMouseMotionListener(new MouseMotionListener() {

@Override
public void mouseDragged(MouseEvent e) {

// do nothing
}

@Override
public void mouseMoved(MouseEvent e) {

// Call the display panel to convey our MouseEvent.
displayPanel.mouseMoved(e);
}
});

}


That should do it! Your program should look something like this when it is finished (note the green square and the mouse pointer).


You can download the full code for this puppy at http://code.google.com/p/tutorialsbytimon/downloads/list or browse the tutorial repository at http://code.google.com/p/tutorialsbytimon

Happy Coding!

Thursday, September 1, 2011

Using a dynamically sized JPanel in a JScrollPane



In this scenario, we insert a dynamically sized JPanel into a JScrollPane. While there are other means of doing this (see Oracle's doc for JScrollPane), we are going to focus on one particular method.

In the following text, we will:

  1. Create a dynamically sized JPanel
  2. Create a form with a JScrollBar on it
  3. Insert the JPanel into the JScrollbar
  4. Review Entire Code


First, we'll create a dynamically sizable extension of JPanel.


Dynamic Panel


/**
* This is the class representing our dynamic panel 

*/
public static class DynamicPanel extends JPanel {

static int width;
static int height;

/**
* CONSTRUCTOR
*/
public DynamicPanel() {
width = 5;
height = 5;

// CRITICAL: the JScrollPane uses the getPreferredSize() method to
// determine how big to make itself. Failing to set preferred size
// will mean that your viewing window will not change size to
// accommodate your new JPanel size.
setPreferredSize(new Dimension(width * (32 + 1), height * (32 + 1)));
}

/**
* Set the width and height of the checkerboard
*
* @param width
* The width of the new checkerboard
* @param height
* The height of the new checkerboard
*/
public void setDimensions(int wide, int high) {

width = wide;
height = high;

// CRITICAL: the JScrollPane uses the getPreferredSize() method to
// determine how big to make itself. Failing to set preferred size
// will mean that your viewing window will not change size to
// accommodate your new JPanel size.
setPreferredSize(new Dimension(width * (32 + 1), height * (32 + 1)));
}

/**
* This method is used to paint images onto the panel.
*
* It overrides the paintComponent method belonging to JPanel
*
* @param g
* This is the canvas on which you will paint
*/
protected void paintComponent(Graphics g) {

// Prevents images from getting gunky / forces full redraw
super.paintComponent(g);

// Initialize brush
g.setColor(Color.red);

// Draw a checkerboard on the screen based on height / weight value
for (int x = 0 ; x < width ; x++) {
for (int y = 0 ; y < height ; y++) {

if ( (0 == x % 2) && 0 == (y % 2) ||
(x % 2 > 0) && (y % 2 > 0 )) {

// draw a 32 x 32 rectangle
g.drawRect(x * 32 + 1, y * 32 + 1, 32, 32);
}
else {
g.fillRect(x * 32 + 1, y * 32 + 1, 32, 32);
}
}
}
}
}

There's a couple of clever tricks in there which you can study further, but the main lines are marked with CRITICAL. For this JPanel extension, setting the *preffered* size of the panel is key to success in our task. Using the setPrefferedSize() method, we set the values which will be used in determining viewport size by the JScrollPane. In short, if you're going to change the size of the panel somehow, don't forget to use setPrefferedSize() method.


To use the setPrefferedSize(Dimension dim) method, you will have to use a Dimension object, but as you can see in the code above, you can create a Dimension object on the fly by feeding in a width and height to the the Dimension constructor.


Next, we'll look at how we display our dynamically sized panel in a JScrollPane.


Scroll Pane w/ Dynamic Panel


/**
* This is the panel which will contain our dynamically sized panel
* in our scrollbar.
*/
public static class DynamicPanelDisplay extends JFrame {

JScrollPane pane = new JScrollPane();
DynamicPanel dynamicPanel = new DynamicPanel();
JPanel inputPanel = new JPanel();

// Fields we'll use to set our width and height.
JTextField txt_width = new JTextField("5");
JLabel lbl_width = new JLabel("Width");

JTextField txt_height = new JTextField("5");
JLabel lbl_height = new JLabel("Height");

JButton btn_update = new JButton("Update");

/**
* CONSTRUCTOR
*/
public DynamicPanelDisplay() {

// Initiate Scroll Pane, plugging in the Dynamic Panel we created
// above.
pane = new JScrollPane(dynamicPanel);

// Set the frame layout and add our scroll pane.
setLayout(new GridLayout(1,2));
add(pane);

// CRITICAL CODE. This is where we call for a size update on
// our checkerboard, and then make the adjustments to display a
// resized JPanel correctly.
btn_update.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {

// Grab our values from the user, then parse into ints.
String str_width = txt_width.getText();
String str_height = txt_height.getText();

int width = Integer.parseInt(str_width);
int height = Integer.parseInt(str_height);

// Reset dimensions of the checkerboard.
dynamicPanel.setDimensions(width, height);

// CRITICAL: call update UI to refresh the look and feel
// of the pane. Failing to do this will yield unpredictable
// visual results.
pane.updateUI();
}
});

// The remaining code is not critical to the exercise at hand. Laying out the
// contents here.
inputPanel.setLayout(new GridLayout(3,2));
inputPanel.add(lbl_width);
inputPanel.add(txt_width);
inputPanel.add(lbl_height);
inputPanel.add(txt_height);
inputPanel.add(btn_update);

// add the input panel to the frame
add(inputPanel);

// Set up our main frame display
setSize(600,400);
setVisible(true);
setTitle("Dynamic Panel Display Tutorial");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);

}
}

In this code, we made the dynamic panel [ dynamicPanel ] a property of this class so we can affect the size from the UI. It is not necessary to affect size this way, through a local instance belonging to the User Interface. In fact, the UI doesn't need to be able to *affect* the panel at all, we merely do this for our conveneince.


What *is* important here is the CRITICAL line, in the update button's [ btn_update ] action performed event listener. We must update the UI every time we resize the panel we are viewing. This is done with the updateUI() method.


You may be changing the size of the dynamic panel from outside of the user interface - and that is just fine. However, you must make sure the ui *knows* about the resizing of the dynamic panel, so it can call updateUI() . Otherwise, you may get some undesirable results.


Screenshots


Here are some screenshots with desirable results:







Final Code


Here is the full and final code used for this tutorial.

If you wish,
you can download this tutorial at http://code.google.com/p/tutorialsbytimon



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package tutorials;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

/**
* This is code is the original work of Timon Davis. Please feel free
* to copy, use and redistribute. Please include this comment
* block, grouped with the code you are re-using, when doing so.
*
* Feel free to modify as you wish, but please add an additional comment
* noting the modifications that have been made.
* @author Timon Davis
*/
public class DynamicPanelTutorial {

/**
* This is the class representing our dynamic panel 

*/
public static class DynamicPanel extends JPanel {

static int width;
static int height;

/**
* CONSTRUCTOR
*/
public DynamicPanel() {
width = 5;
height = 5;

// CRITICAL: the JScrollPane uses the getPreferredSize() method to
// determine how big to make itself. Failing to set preferred size
// will mean that your viewing window will not change size to
// accommodate your new JPanel size.
setPreferredSize(new Dimension(width * (32 + 1), height * (32 + 1)));
}

/**
* Set the width and height of the checkerboard
*
* @param width
* The width of the new checkerboard
* @param height
* The height of the new checkerboard
*/
public void setDimensions(int wide, int high) {

width = wide;
height = high;

// CRITICAL: the JScrollPane uses the getPreferredSize() method to
// determine how big to make itself. Failing to set preferred size
// will mean that your viewing window will not change size to
// accommodate your new JPanel size.
setPreferredSize(new Dimension(width * (32 + 1), height * (32 + 1)));
}

/**
* This method is used to paint images onto the panel.
*
* It overrides the paintComponent method belonging to JPanel
*
* @param g
* This is the canvas on which you will paint
*/
protected void paintComponent(Graphics g) {

// Prevents images from getting gunky / forces full redraw
super.paintComponent(g);

// Initialize brush
g.setColor(Color.red);

// Draw a checkerboard on the screen based on height / weight value
for (int x = 0 ; x < width ; x++) {
for (int y = 0 ; y < height ; y++) {

if ( (0 == x % 2) && 0 == (y % 2) ||
(x % 2 > 0) && (y % 2 > 0 )) {

// draw a 32 x 32 rectangle
g.drawRect(x * 32 + 1, y * 32 + 1, 32, 32);
}
else {
g.fillRect(x * 32 + 1, y * 32 + 1, 32, 32);
}
}
}
}
}

/**
* This is the panel which will contain our dynamically sized panel
* in our scrollbar.
*/
public static class DynamicPanelDisplay extends JFrame {

JScrollPane pane = new JScrollPane();
DynamicPanel dynamicPanel = new DynamicPanel();
JPanel inputPanel = new JPanel();

// Fields we'll use to set our width and height.
JTextField txt_width = new JTextField("5");
JLabel lbl_width = new JLabel("Width");

JTextField txt_height = new JTextField("5");
JLabel lbl_height = new JLabel("Height");

JButton btn_update = new JButton("Update");

/**
* CONSTRUCTOR
*/
public DynamicPanelDisplay() {

// Initiate Scroll Pane, plugging in the Dynamic Panel we created
// above.
pane = new JScrollPane(dynamicPanel);

// Set the frame layout and add our scroll pane.
setLayout(new GridLayout(1,2));
add(pane);

// CRITICAL CODE. This is where we call for a size update on
// our checkerboard, and then make the adjustments to display a
// resized JPanel correctly.
btn_update.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {

// Grab our values from the user, then parse into ints.
String str_width = txt_width.getText();
String str_height = txt_height.getText();

int width = Integer.parseInt(str_width);
int height = Integer.parseInt(str_height);

// Reset dimensions of the checkerboard.
dynamicPanel.setDimensions(width, height);

// CRITICAL: call update UI to refresh the look and feel
// of the pane. Failing to do this will yield unpredictable
// visual results.
pane.updateUI();
}
});

// The remaining code is not critical to the exercise at hand. Laying out the
// contents here.
inputPanel.setLayout(new GridLayout(3,2));
inputPanel.add(lbl_width);
inputPanel.add(txt_width);
inputPanel.add(lbl_height);
inputPanel.add(txt_height);
inputPanel.add(btn_update);

// add the input panel to the frame
add(inputPanel);

// Set up our main frame display
setSize(600,400);
setVisible(true);
setTitle("Dynamic Panel Display Tutorial");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);

}
}

public static void main(String args[]) {

// Create the new frame for display
DynamicPanelDisplay frame = new DynamicPanelDisplay();
}
}

Resources

Oracle's JScrollPane doc

http://download.oracle.com/javase/6/docs/api/javax/swing/JScrollPane.html


Download this file: http://code.google.com/p/tutorialsbytimon/downloads/list