Click here to Skip to main content
Click here to Skip to main content

UI Component Development in Java Swing - Part 1: Design

, 1 Mar 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
A step-by-step guide taking a beginners' approach at effectively creating UI components in Java. This tutorial takes you through the initialization to the completion of an aesthetically pleasing UI component in Java.

Introduction

Java has matured over the years. It has been a decade and a half since its inception, and the language is still going strong and is keen on exploring newer technological paradigms and parlance. Since the coinage of the famed phrase "Write Once, Run Anywhere" (affectionately abbreviated to WORA), the language has broken all traditional programming barriers. Till date, there is no mainstream programming language that can compile code that runs everywhere. Who knew that a simple reinvention of C++ would go on to become the mother of all modern programming languages? In fact, all .NET programming languages owe their existence to the wonderful language that is Java.

Programming in Java

Although this article is written for extreme beginners in Java, it still requires the knowledge of the fundamentals of the language and a little understanding of how the Java Swing framework works. Swing, basically, is a windowing framework that helps developers write code to create Windows and desktop applications. Throughout this article, I will try my best to explain some of the design principles behind Swing as well.

Building custom UI components in Swing

Although the Swing framework has pretty much every UI component available at the developer's disposal, i.e., buttons, labels, text fields, etc., there are still things that one would want to create themselves. It turns out that creating such custom components in Swing is extremely easy to do. And, once you start working your way around the initial confusion, you'll begin enjoying the experience. One of the main concerns that I had before writing this article was probably the fact that there was no decent article available on CodeProject that explained how to create a custom component in Swing. So, here goes.

Every custom component built using the Swing framework has to follow some very important guidelines. The first of which is inheriting the javax.swing.JComponent class. Following is how you would go about creating a custom component:

Listing 1.1:
import javax.swing.JComponent;

public class FancyButton extends JComponent
{
  public FancyButton()
  {
    // .. component construction code comes here
  }
}

In a nutshell, the above code creates a custom component named FancyButton, which identifies itself as a component just by inheriting the javax.swing.JComponent class. You would not be able to run the code, however. The reasons being:

  1. you do not have a main method;
  2. you do not have a window to display your custom component on.

Let's do that now.

Listing 1.2:
import javax.swing.JComponent;
import javax.swing.JFrame;

public class FancyButton extends JComponent
{
  public FancyButton()
  {
    // .. component construction code comes here
  }
  public static void main(String[] args)
  {
    JFrame frame = new JFrame("Test Frame");  // Create a window to display component.
    frame.setSize(400, 400);                  // Set a default window size.
    frame.setLocationRelativeTo(null);        // Brings the window to the center of
                                              // the screen.
    frame.setDefaultCloseOperation(           // Tells the program to terminate if the
      JFrame.EXIT_ON_CLOSE);                  // close [x] button is pressed.
    frame.setVisible(true);                   // Show the window.
  }
}

We have now set the stage for our experiments to begin. In the above code, we create a javax.swing.JFrame, which is basically Java's version of a desktop window with a title and buttons for minimize, restore/maximize, and close. Here we provide the title of the window as "Test Frame", appropriate enough for this tutorial. But, note one important thing: we haven't yet constructed our custom component, nor have we placed it on the frame yet. Let's do that now.

Listing 1.3:
import javax.swing.JComponent;
import javax.swing.JFrame;

public class FancyButton extends JComponent
{
  public FancyButton()
  {

  }
  public static void main(String[] args)
  {
    JFrame frame = new JFrame("Test Frame");
    frame.setSize(400, 400);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    FancyButton button = new FancyButton();  // Initialize the component.
    frame.getContentPane().add(button);      // Place the component on the application
                                             // window such that it fills the whole
                                             // window frame.
    frame.setVisible(true);
  }
}

You'd surely be disappointed if you run the code now. That's because you'd see the same old JFrame as in the previous example and nothing much would have had changed. A reason for that would be that your component is not being painted on the screen. Let's fix this now. Plus, you ought to note the growing imports at the beginning of the program code. In the next update to our code, we'll use dynamic imports with the use of * (asterisk) so that all classes that fall under the java.awt and javax.swing packages get imported without being explicitly imported into the program code. We will also truncate the main(String[]) method for the sake of clarity, but you should include that in your program from the previous example.

Listing 1.4:
import java.awt.*;  // Notice these dynamic imports
import javax.swing.*;

public FancyButton extends JComponent
{
  public FancyButton()
  {
    
  }
  @Override
  public void paintComponent(Graphics graphics)
  {
    graphics.setColor(Color.red);
    graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
  }
  public static void main(String[] args)
  {
    // .. use code from previous code listing 1.3
  }
}

This time when you'll run the code, you'll note that the whole window frame would turn into a red colored surface (see the screenshot below). Everything that turns red on the frame is the area bounds for our newly painted component. Painting on a component is enabled by overriding the paintComponent(Graphics) method in the JComponent class. Notice the explicit use of the @Override annotation before the method declaration. This ensures that the method is properly overridden in the sub-class FancyButton.

Output-002.jpg

Figure 1.1: Screenshot for Listing 1.4

Because our component is spread across the entirety of the window frame, we witness the complete window frame as a block of red.

Within the body of the paintComponent(Graphics) method, we use the acquired java.awt.Graphics objects to paint. The graphics.setColor(Color) method sets the color buffer to red, and in the next line, the graphics object is used again to create a rectangle at point [0, 0] on the window frame with the width and height of the component. This is achieved using the graphics.fillRect(int x, int y, int width, int height) method. Now, since we placed the component right in the center of the window frame, it takes up all the space on the frame, giving the illusion of the window frame turning red throughout.

Understanding the basics of the Graphics class

Now, before we start exploring the process of painting components, we need to learn a bit more about the java.awt.Graphics class. The Graphics class presents us with various methods to paint different shapes, line strokes and fonts on any component or container, thus enabling us to completely design a component from scratch. Hence, by overriding the paintComponent(Graphics) method on any component, we get the possibilities of designing an aesthetically appealing design for our components. Some of the methods this class has to offer are as follows:

Shape fill methods

  • fillRect(Rectangle rectangle)
  • fillRect(int x, int y, int width, int height)
  • fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
  • fillOval(int x, int y, int width, int height)
  • fillPolygon(Polygon polygon)
  • fillPolygon(int[] xPoints, int[] yPoints, int nPoints)

Shape stroke/draw methods

  • drawRect(Rectangle rectangle)
  • drawRect(int x, int y, int width, int height)
  • drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
  • drawOval(int x, int y, int width, int height)
  • drawPolygon(Polygon polygon)
  • drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
  • drawLine(int x1, int y1, int x2, int y2)

Spoiled with choice, we may choose any of these methods to paint pretty shapes to define the design and structure of our component. The difference between the shape fill and shape stroke methods is very simple. Shape fill methods fill the area of a certain shape with the color buffer (the color we set with the setColor(Color) method of the Graphics class). While the shape stroke/draw methods only draw a bounding edge (a border) to the shape. We will find use to almost all these methods over the course of further parts to this tutorial. For now, we would need to define what kind of component we'd like to create.

Since, for the sake of this tutorial, we already named our component FancyButton, you would already have guessed it would be something to do with being a button. But, before we go any further, we need to define what we really want our button to do, or at least how we would want our button to look like. I call this process the Component Design Checklist.

Handy tip # 1: Build yourself a Component Design Checklist

It might sound like a mundane task, but when it comes down to it, there's no better way than to keep tabs on your progress. The best place to start would be to create a simple checklist of do's and dont's. This simple checklist would help us prioritize what needs to be done and what we should not care about.

Component-Design-Checklist-001.jpg

Figure 1.2: A simple Component Design Checklist

An initial attempt at creating a Component Design Checklist. This helps confine my creativity to a simple list of do's and dont's.

Following the checklist I created to confine myself, I made the following amends to Listing 1.4:

Listing 1.5:
import java.awt.*;
import javax.swing.*;

public FancyButton extends JComponent
{
  public FancyButton()
  {
    
  }
  @Override
  public void paintComponent(Graphics graphics)
  {
    graphics.setColor(Color.gray);
    graphics.fillRoundRect(2, 2, this.getWidth() - 4, 
                           this.getHeight() - 4, 30, 30);
  }
  // .. main method truncated - use as in code listing 1.3
}

The amends include changing the color of the component from flashing red to a decent gray so it's easier on the eyes. Instead of fillRect(...), I used a fillRoundRect(...) with arcWidth and arcHeight as 30px. This simple effect gives the button a more pleasing and rounded look. Plus, I managed to add a 2px border around the component by changing the values within the fillRoundRect(...) method. This would help me to later visualize the border settings that I mentioned in my checklist. If you look at the screenshot below, you'll immediately notice the difference.

Output-003.jpg

Figure 1.3: Screenshot for Listing 1.5

This screenshot displays the amends made to the initial code after implementing a few of the actions in my Component Design Checklist. But soon afterwards, you begin to see some new and immediate problems emerge. One such problem is the dithering of the rounded corners. Apparently, this happens because, by default, Java 2D painting/drawing routines do not render anti-aliased output. That should be easy to fix. More than that, this problem goes directly onto my checklist as: Enable anti-alias rendering.

In revisions to follow, we will look at how we solve these individual problems to create an aesthetically pleasing custom UI component for Swing. Stay tuned for more.

History

  • Genesis: 01 March 2010.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Arun Reginald Zaheeruddin
Software Developer (Senior) Plasmoid Technologies
United Kingdom United Kingdom
Arun Reginald Zaheeruddin is a software developer residing in London. His initial forays into the world of software development began in December 1999 when he first got hooked to Java. He still prefers it over other programming languages. Also with a commercial experience of over eight years in C, C++, C#, Objective-C, VB.Net, Python, Ruby, PHP, HTML, Javascript and CSS, he feels it an honour to share his knowledge about the world of programming with people thinking of wading its waters. In his part time, he does laptop repairs at the Computer & Laptop Centre.

Comments and Discussions

 
GeneralIf you need Swing / GUI/ Java development, custom components, layout, look and feel, etc. PinmemberAnn March20-Apr-12 7:05 
GeneralInteresting PinmemberNagy Vilmos23-Mar-11 5:02 
Interesting article. Is there more to come?


Panic, Chaos, Destruction.
My work here is done.

 
or "Drink. Get drunk. Fall over." - P O'H
 
OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre

QuestionPart-2 ? Pinmemberraod13-Jul-10 9:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 1 Mar 2010
Article Copyright 2010 by Arun Reginald Zaheeruddin
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid