Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 6. (The basics) Tabbed Panes. Easy for reading, Click here!

Custom Search
Swing Chapter 6. (The basics) Tabbed Panes. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 3/3 



Previous Page Previous Page (2/3)
Subpages: 1. JTabbedPane
2. Dynamically changeable tabbed pane
3. Customized JTabbedPane and TabbedPaneUI delegate

6.3    Customized JTabbedPane and TabbedPaneUI delegate

Although we intend to save most of our discussion of customizing UI delegates for chapter 21, building fancy-looking tabs is too tempting to pass up, and this example may satisfy your UI customization appetite for now. You can use the techniques shown here to implement custom UI delegates for almost any Swing component. However, there will be major differences from component to component, and this will almost always involve referencing the Swing source code.

First we will build a customized JTabbedPane with two properties: a background image used for each tab, and a background image used for the tabbed pane itself. We will then build a subclass of BasicTabbedPaneUI for use with our customized JTabbedPane, and design it so that it paints the tab background image on each tab. This will require modification of its paint() method. Our custom delegate will also be responsible for painting the tabbed pane background. As we learned in chapter 2 in our discussion of painting, a UI delegate's update() method is used for filling the background of opaque components and then it should pass control to paint(). Both images need to be accessible from our customized JTabbedPane using set and get accessors. (In keeping with the concept of UI delegates not being bound to specific component instances, it would not be a good idea to assign these images as UI delegate properties--unless we were building an image specific L&F.)

BasicTabbedPaneUI is the second largest (in terms of source code) and complex UI delegate. It contains, among other things, its own custom layout manager, TabbedPaneLayout, and a long rendering routine spread out over several different methods. As complex as it is, understanding its inner workings is not required to build on top of it. Since we are concerned only with how it does its rendering, we can narrow our focus considerably. To start, its paint() method calls the paintTab() method which is responsible for painting each tab. This method calls several other methods to perform various aspects of this process (see BasicTabbedPaneUI.java). Briefly, and in order, these methods are:

  paintTabBackground()

  paintTabBorder()

  layoutLabel()

  paintText()

  paintIcon()

  paintFocusIndicator()

By overriding any combination of these methods we can control the tab rendering process however we like. Our customized TabbedPaneUI, which we'll call ImageTabbedPaneUI, overrides the paintTabBackground() method to construct tabs with background images.

Figure 6.2 TabbedPaneDemo with custom tab rendering, LEFT layout

<<file figure6-2.gif>>

Figure 6.3 TabbedPaneDemo with custom tab rendering, BOTTOM layout

<<file figure6-3.gif>>

The Code: TabbedPaneDemo.java

see \Chapter6\2

import java.awt.*;

import java.applet.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.border.*;

import javax.swing.plaf.TabbedPaneUI;

import javax.swing.plaf.ComponentUI;

import javax.swing.plaf.basic.BasicTabbedPaneUI;

public class TabbedPaneDemo extends JApplet

implements ActionListener

{

  // Unchanged code from section 6.2

  public void init() {

    m_loading = new JLabel("Initializing applet...",

      SwingConstants.CENTER);

    getContentPane().add(m_loading);

    Thread initialize = new Thread() {

      public void run() {

        // Unchanged code from section 6.2

        m_tabbedPane = new ImageTabbedPane(

          new ImageIcon("bloo.gif"),

          new ImageIcon("bubbles.jpg"));

        // Unchanged code from section 6.2

      }

    };

    initialize.start();

  }

  public void createTab() {

    // Unchanged code from section 6.2

    label.setBackground(Color.white);

    m_tabbedPane.addTab("Tab #" + m_tabbedPane.getTabCount(),

      m_tabimage, label);

    m_tabbedPane.setForegroundAt(m_tabbedPane.getTabCount()-1,

      Color.white);

    m_tabbedPane.setSelectedIndex(m_tabbedPane.getTabCount()-1);

    setStatus(m_tabbedPane.getSelectedIndex());

  }

  // Unchanged code from section 6.2

}

class ImageTabbedPane extends JTabbedPane

{

  // Display properties

  private Image m_tabBackground;

  private Image m_paneBackground;

  public ImageTabbedPane(ImageIcon tabBackground,

   ImageIcon paneBackground) {

    m_tabBackground = tabBackground.getImage();

    m_paneBackground = paneBackground.getImage();

    setUI((ImageTabbedPaneUI) ImageTabbedPaneUI.createUI(this));

  }

  public void setTabBackground(Image i) {

    m_tabBackground = i;

    repaint();

  }

  public void setPaneBackground(Image i) {

    m_paneBackground = i;

    repaint();

  }

  public Image getTabBackground() {

    return m_tabBackground;

  }

  public Image getPaneBackground() {

    return m_paneBackground;

  }

}

class ImageTabbedPaneUI extends BasicTabbedPaneUI

{

  private Image m_image;

  public static ComponentUI createUI(JComponent c) {

    return new ImageTabbedPaneUI();

  }

  public void update(Graphics g, JComponent c) {

    if (c instanceof ImageTabbedPane) {

      Image paneImage = ((ImageTabbedPane) c).getPaneBackground();

      int w = c.getWidth();

      int h = c.getHeight();

      int iw = paneImage.getWidth(tabPane);

      int ih = paneImage.getHeight(tabPane);

      if (iw > 0 && ih > 0) {

        for (int j=0; j < h; j += ih) {

          for (int i=0; i < w; i += iw) {

            g.drawImage(paneImage,i,j, tabPane);

          }

        }

      }

    }

    paint(g,c);

  }

  public void paint(Graphics g, JComponent c) {

    if (c instanceof ImageTabbedPane)

      m_image = ((ImageTabbedPane) c).getTabBackground();

    super.paint(g,c);

  }

  protected void paintTabBackground(Graphics g, int tabPlacement,

   int tabIndex, int x, int y, int w, int h, boolean isSelected )

  {

    Color tp = tabPane.getBackgroundAt(tabIndex);

    switch(tabPlacement) {

      case LEFT: 

        g.drawImage(m_image, x+1, y+1, (w-2)+(x+1), (y+1)+(h-3),

          0, 0, w, h, tp, tabPane);

        break;

      case RIGHT:

        g.drawImage(m_image, x, y+1, (w-2)+(x), (y+1)+(h-3),

          0, 0, w, h, tp, tabPane);

        break;

      case BOTTOM:

        g.drawImage(m_image, x+1, y, (w-3)+(x+1), (y)+(h-1),

          0, 0, w, h, tp, tabPane);

        break;

      case TOP:

        g.drawImage(m_image, x+1, y+1, (w-3)+(x+1), (y+1)+(h-1),

          0, 0, w, h, tp, tabPane);

    }

  }

}

Understanding the Code

Class TabbedPaneDemo

We have replaced the JTabbedPane instance with an instance of our custom ImageTabbedPane class. This class's constructor takes two ImageIcons as parameters. The first represents the tab background and the second represents the pane background:

        m_tabbedPane = new ImageTabbedPane(

          new ImageIcon("bloo.gif"),

          new ImageIcon("bubbles.jpg"));

We've also modified the createTab() method to make the tab foreground text white because the tab background image we are using is dark.

Class ImageTabbedPane

This JTabbedPane subclass is responsible for keeping two Image variables that represent our custom tab background and pane background properties. The constructor takes two ImageIcons as parameters, extracts the Images, and assigns them to these variables. It calls setUI() to enforce the use of our custom ImageTabbedPaneUI delegate. This class also defines set() and get() accessors for both Image properties.

Class ImageTabbedPaneUI

This class extends BasicTabbedPaneUI and maintains an Image variable for use in painting tab backgrounds. The creatUI() method is overridden to return an instance of itself (remember that this method was used in the ImageTabbedPane constructor):

  public static ComponentUI createUI(JComponent c) {

    return new ImageTabbedPaneUI();

  }

The update() method, as we discussed above, is responsible for filling the pane background and then calling paint(). So we override it to perform a tiling of the pane background image. First we grab a reference to that image check its dimensions. If any are found to be 0 or less we do not go through with painting procedure. Otherwise we tile a region of the pane equal in size to the current size of the ImageTabbedPane itself. Note that in order to get the width and height of an Image we use the following method calls:

      int iw = paneImage.getWidth(tabPane);

      int ih = paneImage.getHeight(tabPane);

(The tabPane reference is protected in BasicTabbedPaneUI and we use it for the ImageObsever here--it is a reference to the JTabbedPane component being rendered.) Once the tiling is done we call paint().

The paint() method simply assigns this class's tab background Image variable to that of the ImageTabbedPane being painted. It then calls its superclass's paint() method which we do not concern ourselves with--let alone fully understand! What we do know is that the superclass's paint() method calls its paintTab() method, which in turn calls its paintTabBackground() method, which is actually responsible for filling the background of each tab. So we overrode this method to use our tab background Image instead. This method specifies four cases based on which layout mode the JTabbedPane is in: LEFT, RIGHT, BOTTOM, TOP. These cases were obtained directly from the BasicTabbedPaneUI source code and we did not modify their semantics at all. What we did modify is what each case does.

We use the drawImage() method of the awt.Graphics class is used to fill a rectangular area defined by the first four int parameters (which represent two points).  The second four int parameters specify an area of the Image (passed in as the first parameter) that will be scaled and mapped onto the above rectangular area. The last parameter represents an ImageObserver instance. 

Note: A more professional implementation would tile the images used in painting the tab background--this implementation assumes that a large enough image will be used to fill a tab's background of any size.

Running the Code:

Figures 6.2 and 6.3 show TabbedPaneDemo in action. To deploy this applet you can use the same HTML file that was used in the previous example. Try out all different tab positions.

Note that the images used are only used within the paint() and update() methods of our UI delegate. In this way we keep with Swing's UI delegate / component separation. One instance of ImageTabbedPaneUI could be assigned to multiple ImageTabbedPane instances using different Images, and there would be no conflicts. Also note that the images are loaded only once (when an instance of ImageTabbedPaneUI is created) so the resource overhead is minimal.

UI Guideline : Custom Tabs Look & Feel

After reading Chapter 21 on Custom Look and Feel , you may like to reconsider customising the Tabbed Pane styles. There are many possibilities which are worthy of consideration. For example the OS2 Warp style of scrolling tabs. This is particularly good where you cannot afford to give away screen space for an additional row or column of tabs. Another possibility is to fix the size of a tab. Currently tabs size of the width of the label which is largely controlled by the length of text and font selected. This lends greater graphical weight to tabs with long labels. The size of the visual target is greater and therefore tabs with long labels are easier to select than tabs with short labels. You may like to consider a tab which fixes a standard size, thus equalising the size of the target and avoiding biasing the usability toward the longer labelled tabs. Further possibilities are tabs with colour and tabs with icons to indicate useful information such as "updated" or "new" or "mandatory input required".



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy