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 15. (Advanced topics) Layered Panes and custom MDI. Easy for reading, Click here!

Custom Search
Swing Chapter 15. (Advanced topics) Layered Panes and custom MDI. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 3/7 



Previous Page Previous Page (2/7) - Next Page (4/7) Next Page
Subpages: 1. JLayeredPane 
2.
Using JLayeredPane to enhance interfaces 
3.
Creating a custom MDI: part I - Dragging panels 
4. Creating a custom MDI: part II - Resizability 
5. Creating a custom MDI: part III - Enhancements 
6. Creating a custom MDI: part IV - Selection and management 
7. Creating a custom MDI: part V - JavaBeans compliance 

15.3  Creating a custom MDI: part I - Dragging panels

JDesktopPane, Swing's version of a multiple document interface (MDI), is the prime example of a complicated layering environment derived from JLayeredPane. One of the best ways to fully leverage the power of JLayeredPane is through the construction of our own MDI environment from scratch. We will end up with a powerful desktop environment that we understand from the inside out and have complete control over. The central component we will be developing, InnerFrame, will ultimately immitate the functionality of JInternalFrame (discussed in the next chapter). In chapter 21, we will continue to develop this component by adding support for all the major look-and-feels, as well as our own custom look-and-feel. The next five sections are centered around this component's construction and proceed in a stepwise fashion.

Note: Due to the complexity of these examples, we suggest you have some time on your hands before attempting to understand each part. Some fragments are too mathematically dense (although not rigorous by any means) to warrant exaustive explanation. In these cases we expect the reader to either trust that it does what we say it does, or bite the bullet and plunge through the code.

We start our construction of InnerFrame by implementing movable, closeable, and iconifiable functionality. LayeredPaneDemo is a simple class we will use throughout each stage of development (with very few modifications) to load several InnerFrames and place them in a JLayeredPane.

Figure 15.4. Custom MDI: part I

<<file figure15-4.gif>>

The Code: LayeredPaneDemo.java

see \Chapter15\2

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import mdi.*;

public class LayeredPaneDemo extends JFrame

{

  public LayeredPaneDemo()

  {

    super("Custom MDI: Part I");

    setSize(570,400);

    getContentPane().setBackground(new Color(244,232,152));

    getLayeredPane().setOpaque(true);

    InnerFrame[] frames = new InnerFrame[5];

    for(int i=0; i<5; i++) {

      frames[i] = new InnerFrame("InnerFrame " + i);

      frames[i].setBounds(50+i*20, 50+i*20, 200, 200);

      getLayeredPane().add(frames[i]);

    }

    WindowListener l = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);

      }

    };

    Dimension dim = getToolkit().getScreenSize();

    setLocation(dim.width/2-getWidth()/2,

      dim.height/2-getHeight()/2);

    ImageIcon image = new ImageIcon("spiral.gif");

    setIconImage(image.getImage());

    addWindowListener(l);

    setVisible(true);

  }

  public static void main(String[] args) {

    new LayeredPaneDemo();

  }

}

The Code: InnerFrame.java

see \Chapter15\2\mdi

package mdi;

import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.border.EmptyBorder;

public class InnerFrame

extends JPanel

{

  private static String IMAGE_DIR = "mdi" + java.io.File.separator;

  private static ImageIcon ICONIZE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"iconize.gif");

  private static ImageIcon RESTORE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"restore.gif");

  private static ImageIcon CLOSE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"close.gif");

  private static ImageIcon PRESS_CLOSE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"pressclose.gif");

  private static ImageIcon PRESS_RESTORE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"pressrestore.gif");

  private static ImageIcon PRESS_ICONIZE_BUTTON_ICON =

    new ImageIcon(IMAGE_DIR+"pressiconize.gif");

  private static final int WIDTH = 200;

  private static final int HEIGHT = 200;

  private static final int TITLE_BAR_HEIGHT = 25;

  private static Color TITLE_BAR_BG_COLOR =

    new Color(108,190,116);

  private String m_title;

  private JLabel m_titleLabel;

  private boolean m_iconified;

  private JPanel m_titlePanel;

  private JPanel m_contentPanel;

  private JPanel m_buttonPanel;

  private JPanel m_buttonWrapperPanel;

  private InnerFrameButton m_iconize;

  private InnerFrameButton m_close;

  public InnerFrame(String title) {

    m_title = title;

    setLayout(new BorderLayout());

    createTitleBar();

    m_contentPanel = new JPanel();

    add(m_titlePanel, BorderLayout.NORTH);

    add(m_contentPanel, BorderLayout.CENTER);

  }

  public void toFront() {

    if (getParent() instanceof JLayeredPane)

      ((JLayeredPane) getParent()).moveToFront(this);

  }

  public void close() {

    if (getParent() instanceof JLayeredPane) {

      JLayeredPane jlp = (JLayeredPane) getParent();

      jlp.remove(InnerFrame.this);

      jlp.repaint();

    }

  }

  public void setIconified(boolean b) {

    m_iconified = b;

    if (b) {

      setBounds(getX(), getY(), WIDTH, TITLE_BAR_HEIGHT);

      m_iconize.setIcon(RESTORE_BUTTON_ICON);

      m_iconize.setPressedIcon(PRESS_RESTORE_BUTTON_ICON);

    }

    else {

      setBounds(getX(), getY(), WIDTH, HEIGHT);

      m_iconize.setIcon(ICONIZE_BUTTON_ICON);

      m_iconize.setPressedIcon(PRESS_ICONIZE_BUTTON_ICON);

      revalidate();

    }

  }

  public boolean isIconified() {

    return m_iconified;

  }

  ////////////////////////////////////////////

  //////////////// Title Bar /////////////////

  ////////////////////////////////////////////

  // create the title bar m_titlePanel

  public void createTitleBar() {

    m_titlePanel = new JPanel() {

      public Dimension getPreferredSize() {

        return new Dimension(InnerFrame.WIDTH,

          InnerFrame.TITLE_BAR_HEIGHT);

      }

    };

    m_titlePanel.setLayout(new BorderLayout());

    m_titlePanel.setOpaque(true);

    m_titlePanel.setBackground(TITLE_BAR_BG_COLOR);

    m_titleLabel = new JLabel(m_title);

    m_titleLabel.setForeground(Color.black);

    m_close = new InnerFrameButton(CLOSE_BUTTON_ICON);

    m_close.setPressedIcon(PRESS_CLOSE_BUTTON_ICON);

    m_close.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        InnerFrame.this.close();

      }

    });

    m_iconize = new InnerFrameButton(ICONIZE_BUTTON_ICON);

    m_iconize.setPressedIcon(PRESS_ICONIZE_BUTTON_ICON);

    m_iconize.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        InnerFrame.this.setIconified(

          !InnerFrame.this.isIconified());

      }

    });

    m_buttonWrapperPanel = new JPanel();

    m_buttonWrapperPanel.setOpaque(false);

    m_buttonPanel = new JPanel(new GridLayout(1,2));

    m_buttonPanel.setOpaque(false);

    m_buttonPanel.add(m_iconize);

    m_buttonPanel.add(m_close);

    m_buttonPanel.setAlignmentX(0.5f);

    m_buttonPanel.setAlignmentY(0.5f);

    m_buttonWrapperPanel.add(m_buttonPanel);

    m_titlePanel.add(m_titleLabel, BorderLayout.CENTER);

    m_titlePanel.add(m_buttonWrapperPanel, BorderLayout.EAST);

    InnerFrameTitleBarMouseAdapter iftbma =

      new InnerFrameTitleBarMouseAdapter(this);

    m_titlePanel.addMouseListener(iftbma);

    m_titlePanel.addMouseMotionListener(iftbma);

  }

  // title bar mouse adapter for frame dragging

  class InnerFrameTitleBarMouseAdapter

  extends MouseInputAdapter

  {

    InnerFrame m_if;

    int m_XDifference, m_YDifference;

    boolean m_dragging;

    public InnerFrameTitleBarMouseAdapter(InnerFrame inf) {

      m_if = inf;

    }

    public void mouseDragged(MouseEvent e) {

      if (m_dragging)

        m_if.setLocation(e.getX()-m_XDifference + getX(),

          e.getY()-m_YDifference + getY());

    }

    public void mousePressed(MouseEvent e) {    

      m_if.toFront();

      m_XDifference = e.getX();

      m_YDifference = e.getY();

      m_dragging = true;

    }

    public void mouseReleased(MouseEvent e) {

      m_dragging = false;

    }

  }

  // custom button class for title bar

  class InnerFrameButton extends JButton

  {

    Dimension m_dim;

    public InnerFrameButton(ImageIcon ii) {

      super(ii);

      m_dim = new Dimension(

        ii.getIconWidth(), ii.getIconHeight());

      setOpaque(false);

      setContentAreaFilled(false);

      setBorder(null);

    }

    public Dimension getPreferredSize() {

      return m_dim;

    }

    public Dimension getMinimumSize() {

      return m_dim;

    }

    public Dimension getMaximumSize() {

      return m_dim;

    }

  }

}

Understanding the Code:

Class LayeredPaneDemo

We are familiar with most of the code in this class. Note that the mdi package is imported, which is the package InnerFrame resides in. The only purpose of LayeredPaneDemo is to provide a container for several InnerFrames. An array of five InnerFrames is created and they are placed in our JFrame's layered pane, at the default layer, in a cascading fashion. Note that the layered pane's opaque property has been explicity enabled (setOpaque(true)). This is to ensure smooth repainting when dragging our InnerFrames by guaranteeing that all layered pane pixels will be painted (see chapter 2 for more about opacity).

Class mdi.InnerFrame

This is our custom internal frame we will be expanding on throughout the remainder of this chapter. It is defined within the mdi package and extends JPanel.

Class variables:

String IMAGE_DIR: directory header string used to locate images in the mdi package

ImageIcon ICONIZE_BUTTON_ICON: icon used for the iconify button =  [black interior]

ImageIcon RESTORE_BUTTON_ICON: icon used for the iconfy button when in the iconified state (representing de-iconify) =  [black interior]

ImageIcon CLOSE_BUTTON_ICON: icon used for the close button =  [black interior]

ImageIcon PRESS_CLOSE_BUTTON_ICON: icon used for the close button when it is in the pressed state =  [dark green interior]

ImageIcon PRESS_RESTORE_BUTTON_ICON: icon used for the iconfy button in the iconified state (representing de-iconify) when also in the pressed state =  [dark green interior]

ImageIcon PRESS_ICONIZE_BUTTON_ICON: icon used for the iconify button when in the pressed state =  [dark green interior]

Color C_BUTTONBACK: the titlebar button background color

Several instance variables are also necessary:

int WIDTH: InnerFrame's width.

int HEIGHT: InnerFrame's height.

int TITLE_BAR_HEIGHT: default height of the title bar.

Color TITLE_BAR_BG_COLOR: default Color the title bar.

Instance variables

String m_title: title bar String.

JLabel m_titleLabel: label contained in the title bar displaying the title string, m_title.

boolean m_iconified:  true when in the iconified state.

JPanel m_titlePanel:  panel representing the title bar.

JPanel m_contentPanel:  the central InnerFrame container.

JPanel m_buttonPanel: small panel for frame buttons.

JPanel m_buttonWrapperPanel: panel to surround m_buttonPanel to allow correct alignment.

InnerFrameButton m_iconize: custom frame button for iconification/de-iconification.

InnerFrameButton m_close: custom frame button for closing.

The InnerFrame constructor takes a title String as its only parameter and sets the title variable accordingly. We use a BorderLayout and call our createTitleBar() method to initialize m_titlePanel, which represents our title bar (see below). We then initialize m_contentPanel which acts as our frame's central container. We add these components to InnerFrame in the NORTH and CENTER regions respectively.

The toFront() method is responsible for moving InnerFrame to the frontmost position in its layer. This is called from m_titlePanel's mouse adapter when the title bar is pressed, as we will see below. This will only function if InnerFrame is within a JLayeredPane (or JLayeredPane subclass--such as JDesktopPane).

The close() method is responsible for removing an InnerFrame from its parent container. This is called when the close button, m_close, is clicked within the title bar. We specifically only allow removal to work from within a JLayeredPane. If this component is added to any other container we are assuming that it is not meant for removal in this fashion. We also keep a reference to the parent layered pane so that it can be repainted after we have been removed.

The setIconified() method controls iconification and deiconification (often called restore) and reduces the height of InnerFrame to that of the title bar. When an iconification occurs we change the m_iconize button's regular and pressed state icons to those representing de-iconification. Similarly, when a de-iconify occurs we restore the original height of InnerFrame and replace the icons with those representing iconification.

The createTitleBar() method is responsible for populating and laying out m_titlePanel, our title bar. It starts by initializing m_titlePanel with a preferred size according to our WIDTH and TITLE_BAR_HEIGHT constants. m_titlePanel uses a BorderLayout and its background is set to TITLE_BAR_BG_COLOR. (Note that these hard-coded values will be replaced by variables in future stages of development.) m_titleLabel is created with the m_title String that was passed to the InnerFrame constructor.

The m_close button is an instance of InnerFrameButton (see below) and uses custom ImageIcon's for normal and pressed states. An ActionListener is added to simply invoke close() when this button is pressed. The m_iconize button is created in a similar fashion, except its ActionListener is constructed to invoke the setIconified() method. Once these buttons are constructed, we place them in m_buttonPanel useing a GridLayout, and place this panel within our m_buttonWrapperPanel which uses a FlowLayout. This allows for perfect button alignment, using JComponent's setAlignmentX/Y() methods, while enforcing equal size among buttons no greater than their preferred sizes.

Finally, the createTitleBar() method ends by creating an InnerFrameTitleBarMouseAdapter and attaches it to m_titlePanel.

Class mdi.InnerFrame.InnerFrameTitleBarMouseAdapter

This class extends javax.swing.event.MouseInputAdapter and holds a reference to the InnerFrame it is associated with. The function of mousePressed() is to obtain the base coordinate offsets, m_XDifference and m_YDifference, for use in the mouseDragged method, and set the m_dragging flag to true (because a mouse press always precedes a mouse drag). The mouseDragged() method is called whenever an InnerFrame is dragged. This method starts by testing whether the m_dragging flag is set to true. If it is InnerFrame is moved to a new location. To calculate the new position as it is dragged this method uses the current InnerFrame coordinates, getX() and getY(), plus the difference of the mouseDragged() event coordinates, e.getX() and e.getY(), and the base offset coordinates, m_XDifference and m_YDifference. These base offset coordinates, initialized within the mousePressed() method, are necessary to keep mouse position with regard to the upper left-hand corner of InnerFrame remains constant. The mouseReleased() method's only function is to set the m_dragging flag to false when the mouse button is released.

Class mdi.InnerFrame.InnerFrameButton

The InnerFrameButton class extends JButton and its constructor takes an ImageIcon parameter. It uses this icon to construct a Dimension instance  which is in turn used for the button's minimum, maximum, and preferred sizes. The button is then set to non-opaque, and we call setContentAreaFilled(false) to guarantee that its background will never be filled. We also remove its border to give it an embedded look.

Running the Code

Figure 15.4 shows LayeredPaneDemo in action. Experiment with moving, iconifying, de-iconifying and closing our custom frames. Note that a frame can be selected even when its title bar lies behind the content of another frame. Mouse events pass right through our frame's content panels. This is just one of the many flaws that need to be accounted for. Also notice that we can drag any frame completely outside of the layered pane. The most immediately noticeable functionality that InnerFrame lacks is resizability. This is the single most difficult feature to implement, and is the subject of the next section.



[ 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