A custom brush is one based on a bitmap that you load from a file or create in memory. The CustomBrush sample application demonstrates how to create and use custom brushes.
The CustomBrush application consists of two panels and two buttons. The first of the panels (panelGrid) consists of 64 squares. When the user clicks on one of these squares, the application registers the fact that the square has been clicked, paints the square black, and performs a bitwise operations on one of the members of an array of short integers. The integer that is modified depends on which square was clicked.
When the user clicks the Test button, the CustomBrush application creates a bitmap based on the array of short integers, creates a brush based on that bitmap, and then uses that brush to paint the panel (panelRect) that appears above the Test button.
Upon application startup, the CustomBrush application performs the following tasks:
public class Form1 extends Form
{
// Rectangles into which to divide the panel.
Rectangle [] rgRects = new Rectangle[64];
// Tracks the state of the rectangles.
boolean [] rgStates = new boolean[64];
int [] bBrushBits = new int[8];
public Form1()
{
// Set the state array to an initial value of false.
for(int i = 0; i < rgStates.length; i++){
rgStates[i] = false;
}
// Initialize the form.
initForm();
// Divide the panel into 64 rectangles and paint the form.
rgRects = getRects(panelGrid.getClientRect(), 8, 8);
this.invalidate();
}
/**
* This method divides a specified rectangular area into the specified
* number of subrectangles (down and across), and returns an array
* containing the subrectangles.
*/
private Rectangle [] getRects(Rectangle rcClient, int nAcross, int nDown){
int deltaX, deltaY;
int x, y, right, bottom;
int i;
Rectangle rgRects[] = new Rectangle[nAcross * nDown];
// Store the right and bottom of the rectangle.
right = rcClient.getRight();
bottom = rcClient.getBottom();
// Determine the height and width of each rectangle.
deltaX = (right - rcClient.x) / nAcross;
deltaY = (bottom - rcClient.y) / nDown;
// Initialize the array of cell rectangles.
for(y = rcClient.y, i = 0; y < bottom; y += deltaY){
for(x = rcClient.x; x < (right - nAcross) && i < (nAcross * nDown);
x += deltaX, i++){
rgRects[i] = new Rectangle(x, y, deltaX, deltaY);
}
}
return rgRects;
}
When the grid panel’s paint event handler is invoked, it uses the dimensions stored in the application’s Rectangle array (rgRects) and the states stored in the class-level boolean array (rgStates) to draw a grid. If the boolean value corresponding to a given rectangle is true, the rectangle is painted black. Otherwise, it is painted with a null brush. This enables the user to determine the appearance of the brush that is eventually created based on the pattern of the rectangles in the grid panel:
private void panelGrid_paint(Object source, PaintEvent e)
{
e.graphics.setPen(new Pen(Color.BLACK, PenStyle.SOLID, 1));
// Draw the grid to reflect the current state of the
// squares.
for(int i = 0; i < rgRects.length; i++){
if(rgStates[i] == true){
// If the square has been previously clicked, fill it.
e.graphics.setBrush(new Brush(Color.BLACK));
}else{
e.graphics.setBrush(Brush.NULL);
}
e.graphics.drawRect(rgRects[i]);
}
}
Each time the user clicks on one of the rectangles in the grid panel, the panel’s click event is fired. Within the grid panel’s click event handler, the application determines which rectangle was clicked, sets the corresponding boolean state variable to true, and calls panelGrid.invalidate to force a repaint. Additionally, the application performs a bitwise operation on the appropriate member of the array of bits stored in the class-level bBrushBits variable:
private void panelGrid_mouseDown(Object source, MouseEvent e)
{
Graphics gr = this.createGraphics();
gr.setBrush(new Brush(Color.BLACK));
Integer nBit = new Integer(0);
// Determine which cell was clicked.
for(int i = 0; i < 64; i++){
Rectangle rc = new Rectangle(rgRects[i].x,
rgRects[i].y, rgRects[i].height,
rgRects[i].width);
if(rc.contains(new Point(e.x, e.y))){
// Set the appropriate boolean state variable.
rgStates[i] = true;
// Perform a bitwise NOT operation on the appropriate
// integer in the array of shorts on which we’ll base
// our bitmap.
if(i % 8 == 0)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x80;
if(i % 8 == 1)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x40;
if(i % 8 == 2)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x20;
if(i % 8 == 3)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x10;
if(i % 8 == 4)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x08;
if(i % 8 == 5)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x04;
if(i % 8 == 6)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x02;
if(i % 8 == 7)
bBrushBits[i/8] = bBrushBits[i/8] ^ 0x01;
break;
}
}
// Repaint the grid.
panelGrid.invalidate();
}
Finally, when the user clicks the Test button, the test button’s click event handler forces a repaint of the panel (panelRect) that appears above the Test button by calling panelRect.invalidate:
private void btnTest_click(Object source, Event e)
{
panelRect.invalidate();
}
Within the panelRect paint handler, the application creates a bitmap based on the array of short integers (bBrushBits), creates a brush based on the bitmap, and paints the panel (panelRect) that appears above the Test button:
private void panelRect_paint(Object source, PaintEvent e)
{
short [] rgShorts = new short[8];
for(int i = 0; i < bBrushBits.length; i++){
rgShorts[i] = (short) bBrushBits[i];
}
e.graphics.setBrush(new Brush(new Bitmap(8,8,1,1,rgShorts)));
e.graphics.fill(panelRect.getClientRect());
}