java超级链接和启动浏览器



超级链接和启动浏览器

许多程序都会在 “about” 对话框中呈现超级链接。单击超级链接时,程序将启动默认浏览器,并为用户打开应用程序的网站。我想 “about” 对话框中的超级链接是可以用类来描述的,所以我创建了一个 DE>AboutBox3DE> 应用程序来说明其可行性。请阅读以下代码:

 DE>AboutBox (JFrame frame, String centerMode)
{
   super (frame, "AboutBox", true /* modal */);

   // Create a pane that presents this dialog box's text. Surround the pane
   // with a 5-pixel empty border.

   Pane pane = new Pane (5);
   pane.setPreferredSize (new Dimension (250, 100));

   // Create a title with a drop shadow for the pane.

   Font font = new Font ("Verdana", Font.BOLD, 32);
   Pane.TextNode tn = pane.new TextNode ("About Box", font, Color.blue,
                                         Pane.TextNode.CENTERX,
                                         Pane.TextNode.CENTERY, Color.black);
   pane.add (tn);

   // Create a link for the pane.

   font = new Font ("Verdana", Font.BOLD, 12);
   tn = pane.new TextNode ("Jeff Friesen", font, Color.blue,
                           Pane.TextNode.CENTERX, 80,
                           null, "http://www.javajeff.mb.ca", Color.red);
   pane.add (tn);

   // Add pane to the center region of the dialog box's content pane.

   getContentPane ().add (pane);

   // Create a button for disposing the dialog box.

   final JButton btnOk = new JButton ("Ok");
   btnOk.addActionListener (new ActionListener ()
                            {
                                public void actionPerformed (ActionEvent e)
                                {
                                   dispose ();
                                }
                            });

   // Add button via an intermediate panel that causes button to be laid
   // out at its preferred size to the south region of the dialog box's
   // content pane.

   getContentPane ().add (new JPanel () {{ add (btnOk); }},
                          BorderLayout.SOUTH);

   // Resize all components to their preferred sizes.

   pack ();

   // Center the dialog box with respect to the frame window or the screen.


   setLocationRelativeTo (centerMode.equals ("W") ? frame : null);
}DE>

DE>AboutBox(JFrame frame, String centerMode)DE>构造函数创建了一个 DE>PaneDE> 组件,来描述一个用于绘制文字的区域。该组件的 DE>Pane(int borderSize)DE> 构造函数使用 DE>borderSizeDE> 参数,来识别组件边框的大小(以像素为单位)– 绘制区域的大小等于 Pane 的大小减去边框大小:

 DE>Pane (int borderSize)
{
   // Create a solid color border that both surrounds and is part of the
   // this component. Select the panel background color that is appropriate
   // to this look and feel.

   setBorder (new MatteBorder (borderSize, borderSize, borderSize,
                               borderSize,
                               UIManager.getColor ("Panel.background")));
}DE>

该组件将文字存储为 DE>Pane.TextNodeDE> 对象的数组列表。每个 DE>TextNodeDE> 描述一个文字条目,并且创建自三个构造函数之一。最简单的构造函数是 DE>TextNode(String text, Font font, Color color, int x, int y)DE>,它用于创建一个非超级链接且没有阴影的文字节点。使用的五个参数是:

  • DE>textDE> 指定要绘制的文字
  • DE>fontDE> 指定要使用的字体
  • DE>fontDE> 指定要使用的文字颜色
  • DE>xDE> 指定第一个字符的起始列
  • DE>yDE> 指定每个字符基线所在的行

第二简单的构造函数是:DE>TextNode(String text, Font font, Color color, int x, int y, Color shadowColor)DE>.除了上述参数外,还需要为该函数指定 DE>shadowColorDE>,即阴影的颜色。如果传递 DE>nullDE>,则不呈现阴影,这样一来该构造函数就与前一构造函数一样了。这两个构造函数都调用下面的第三个构造函数:

 DE>TextNode (String text, Font font, Color color, int x, int y,
          Color shadowColor, String url, Color activeLinkColor)
{
   this.text = text;
   this.font = font;
   this.color = color;
   this.x = x;
   this.y = y;
   this.shadowColor = shadowColor;   this.url = url;
   this.activeLinkColor = activeLinkColor;

   if (url != null)

   {
       addMouseListener (new MouseAdapter ()
                         {
                             public void mousePressed (MouseEvent e)
                             {
                                int mx = e.getX ();
                                int my = e.getY ();

                                if (mx >= TextNode.this.x &&

                                    mx < TextNode.this.x+width &&

                                    my > TextNode.this.y-height &&

                                    my <= TextNode.this.y)
                                {
                                    active = false;
                                    repaint ();
                                }
                             }

                             public void mouseReleased (MouseEvent e)
                             {
                                int mx = e.getX ();
                                int my = e.getY ();

                                if (mx >= TextNode.this.x &&

                                    mx < TextNode.this.x+width &&

                                    my > TextNode.this.y-height &&

                                    my <= TextNode.this.y)
                                {
                                    active = true;
                                    repaint ();
                                    Launcher.
                                      launchBrowser (TextNode.this.url);
                                }
                             }
                         });

       addMouseMotionListener (new MouseMotionListener ()
                               {
                                   public void mouseMoved (MouseEvent e)
                                   {
                                      int mx = e.getX ();
                                      int my = e.getY ();

                                      if (mx >= TextNode.this.x &&

                                          mx < TextNode.this.x+width &&

                                          my > TextNode.this.y-height &&

                                          my <= TextNode.this.y)
                                      {
                                          if (!active)
                                          {
                                              active = true;
                                              repaint ();
                                          }
                                      }
                                      else
                                      {
                                          if (active)
                                          {
                                              active = false;
                                              repaint ();
                                          }
                                      }
                                   }

                                   public void mouseDragged (MouseEvent e)
                                   {
                                   }
                               });
   }
}DE>

保存参数后,该构造函数会在 Pane 中注册一个鼠标监听器(假设 DE>urlDE> 不为 DE>nullDE>)。这些侦听器将判断鼠标指针是否位于超级链接文本上。如果是,则操纵 DE>activeDE> 变量,将呈现该节点及其他节点,并启动浏览器。

 DE>class Launcher
{
   static void launchBrowser (String url)
   {
      try
      {
          // Identify the operating system.

          String os = System.getProperty ("os.name");

          // Launch browser with URL if Windows. Otherwise, just output the url
          // to the standard output device.

          if (os.startsWith ("Windows"))
              Runtime.getRuntime ()
                     .exec ("rundll32 url.dll,FileProtocolHandler " + url);
          else
              System.out.println (url);
      }
      catch (IOException e)
      {
          System.err.println ("unable to launch browser");
      }
   }
}DE>

DE>PaneDE> 的 DE>public void paintComponent(Graphics g)DE> 方法将在该组件及其文本节点呈现的时候被调用。该方法启用反失真技术(防止文字出现锯齿),获取组件的插入位置(所以文本不会落在边框上),清理绘制区域使其成为白色,并呈现数组列表存储的每个文字节点。

 DE>public void paintComponent (Graphics g)
{
   // Prevent jagged text.

   ((Graphics2D) g).setRenderingHint (RenderingHints.KEY_ANTIALIASING,
                                      RenderingHints.VALUE_ANTIALIAS_ON);

   // Because the border is part of the panel, we need to make sure that we
   // don't draw over it.

   Insets insets = getInsets ();

   // Paint everything but the border white. 

   g.setColor (Color.white);
   g.fillRect (insets.left, insets.top, getWidth ()-insets.left-insets.right,
               getHeight ()-insets.top-insets.bottom);

   // Render all nodes.

   Iterator iter = nodes.iterator ();
   while (iter.hasNext ())
   {
      TextNode tn = (TextNode) iter.next ();

      tn.render (g, insets);
   }
}DE>

每个文本节点都是由 DE>TextNodeDE> 的 DE>void render(Graphics g, Insets insets)DE> 方法呈现的。该方法首先确定了字体,然后调用私有的 DE>strDim()DE> 方法来获取要绘制文字的规格尺寸等。然后呈现文本(可以选择阴影、超级链接等属性):

 DE>void render (Graphics g, Insets insets)
{
   g.setFont (font);

   Dimension d = strDim (g, text);
   width = (int) d.width;
   height = (int) d.height;

   // Always drop the drop shadow (if specified) first.

   if (shadowColor != null)
   {
       g.setColor (shadowColor);

       if (x == CENTERX)
           x = (getWidth ()-d.width)/2;

       if (y == CENTERY)
           y = insets.top+(getHeight ()-insets.bottom-insets.top)/2;

       // Draw the drop shadow.

       g.drawString (text, x+SHADOW_OFFSET, y+SHADOW_OFFSET);
   }

   // If the text is not a link, active can never be true -- the mouse
   // listeners are not installed.

   g.setColor ((active) ? activeLinkColor: color);

   // If a drop shadow was drawn, x and y will never equal CENTERX and
   // CENTERY (respectively). This is okay because x and y must contain
   // the same values as specified when drawing the drop shadow.

   if (x == CENTERX)
       x = (getWidth ()-d.width)/2;

   if (y == CENTERY)
       y = insets.top+(getHeight ()-insets.bottom-insets.top)/2;

   // Draw the text.

   g.drawString (text, x, y);
}DE>

DE>g.setColor ((active) ? activeLinkColor: color);DE> 决定是否绘制有效的超级链接文本(使用由DE>activeLinkColorDE> 指定的有效链接颜色),或者使用由 DE>colorDE> 指定的颜色绘制无效超级链接文本(或非超级链接文本)。图 2 显示了此决定的结果:


超级链接和启动浏览器 - alpsdyk2001 - 编程学习!

图 2 当鼠标指针移动到文本上时,超级链接的文本变成了红色。单击可查看大图。

状态栏

许多应用程序都会呈现状态栏,以显示程序的名称和版本、相应于菜单的帮助文字、当前时间以及一些其他信息。由于状态栏如此有用,您可能认为 Java 包含了一个 DE>javax.swing.JStatusBarDE> 组件。然而,事实并非如此。幸运的是,创建自己的状态栏还算容易,如图 3 所示。

超级链接和启动浏览器 - alpsdyk2001 - 编程学习!

图 3. 状态栏中当前菜单项的帮助文字

图 3 显示了一个由 DE>StatBarDE> 应用程序创建的状态栏。该应用程序将一个 DE>javax.swing.JLabelDE> 组件和一个 DE>javax.swing.event.MenuListenerDE> 结合在一起,然后使用 DE>java.awt.event.MouseListenerDE> 显示相应于菜单的菜单项帮助文字 — 或者在未选择菜单或菜单项时显示默认文字。请看列表 3。

列表 3. StatBar.java

 DE>// StatBar.java

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;

public class StatBar extends JFrame
{
   // The status label serves as this application's status bar. A description
   // of the currently highlighted menu/item appears on the status bar.

   JLabel status;

   // The default text appears on the status bar at program startup, and when
   // no other menu/item text appears.

   String defaultStatusText = "Welcome to StatBar 1.0!";

   // The MenuItem helper class conveniently organizes the menu items for each
   // of the File and Edit menus. This organization reduces the amount of
   // source code that appears in the StatBar() constructor, which hopefully
   // makes it easier to study the constructor, and facilitates adding extra
   // menu items in the future.

   class MenuItem
   {
      String label;      // menu text
      ActionListener al;      String desc;       // menu description for status bar

      MenuItem (String label, ActionListener al, String desc)
      {
         this.label = label;
         this.al = al;
         this.desc = desc;
      }
   }

   // Construct StatBar's GUI and indirectly start AWT helper threads.

   public StatBar (String title)
   {
      // Pass application title to superclass, so that it appears on the title
      // bar.

      super (title);

      // When the user initiates a close operation from the System menu or by
      // clicking the tiny x window on a Microsoft Windows' window title bar,
      // terminate this application.

      setDefaultCloseOperation (EXIT_ON_CLOSE);

      // Construct the application's menu bar.
      JMenuBar mb = new JMenuBar ();

      // Create a menu listener shared by all menus on the menu bar. This menu
      // listener either displays default text or menu-specific text on the
      // status bar.

      MenuListener menul;
      menul = new MenuListener ()
      {
         public void menuCanceled (MenuEvent e)
         {            
         }

         public void menuDeselected (MenuEvent e)
         {            
            status.setText (defaultStatusText);
         }

         public void menuSelected (MenuEvent e)
         {
            JMenu m = (JMenu) e.getSource ();
            status.setText (m.getActionCommand ());
         }
      };

      // Create a mouse listener shared by all menu items on all menus. This
      // mouse listener displays menu-item specific text on the status bar
      // whenever the mouse pointer enters the menu item. It displays default
      // text when the mouse pointer exits a menu item.

      MouseListener statusl = new MouseAdapter ()
      {
         public void mouseEntered (MouseEvent e)
         {
            JMenuItem mi = (JMenuItem) e.getSource ();
            status.setText (mi.getActionCommand ());
         }

         public void mouseExited (MouseEvent e)
         {
            status.setText (defaultStatusText);
         }
      };

      // The first menu to appear on the menu bar is File. The user invokes
      // menu items on this menu to open, save, and print documents, and to
      // terminate the application.

      JMenu menuFile = new JMenu ("File");
      menuFile.addMenuListener (menul);
      menuFile.setActionCommand ("Open document, save changes, print document "
                                 + "and terminate StatBar.");

      // Create a listener for each menu item on the File menu.

      ActionListener openl;
      openl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Open listener invoked.");
         }
      };

      ActionListener saveasl;
      saveasl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Save as listener invoked.");
         }
      };

      ActionListener savel;
      savel = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)

         {
            System.out.println ("Save listener invoked.");
         }
      };

      ActionListener printl;
      printl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Print listener invoked.");
         }
      };

      ActionListener exitl;
      exitl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.exit (0);
         }
      };

      // Identify menu items to be installed on the File menu.

      MenuItem [] itemsFile =
      {
         new MenuItem ("Open...", openl, "Open a document."),
         new MenuItem ("Save", savel, "Save changes to current document."),
         new MenuItem ("Save as...", saveasl, "Save current document to new "
                       + "document."),
         new MenuItem ("Print...", printl, "Print current document."),
         new MenuItem (null, null, null),
         new MenuItem ("Exit", exitl, "Terminate StatBar.")
      };

      // Install all of the previous menu items on the File menu.

      for (int i = 0; i < itemsFile.length; i++)
      {
           if (itemsFile [i].label == null)
           {
               menuFile.addSeparator ();
               continue;
           }

           JMenuItem mi = new JMenuItem (itemsFile [i].label);
           mi.addActionListener (itemsFile [i].al);
           mi.setActionCommand (itemsFile [i].desc);
           mi.addMouseListener (statusl);
           menuFile.add (mi);
      }

      // Add the file menu to the menu bar.

      mb.add (menuFile);

      // The second menu to appear on the menu bar is Edit. The user invokes
      // menu items on this menu to undo any changes and perform copy/cut/paste
      // operations on the current document.

      JMenu menuEdit = new JMenu ("Edit");
      menuEdit.addMenuListener (menul);
      menuEdit.setActionCommand ("Perform various editing tasks and undo " +
                                 "changes.");

      // Create a listener for each menu item on the Edit menu.

      ActionListener undol;
      undol = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Undo listener invoked.");
         }
      };

      ActionListener copyl;
      copyl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Copy listener invoked.");
         }
      };

      ActionListener cutl;
      cutl = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Cut listener invoked.");
         }
      };

      ActionListener pastel;
      pastel = new ActionListener ()
      {
         public void actionPerformed (ActionEvent e)
         {
            System.out.println ("Paste listener invoked.");
         }
      };

      // Identify menu items to be installed on the Edit menu.

      MenuItem [] itemsEdit =
      {
         new MenuItem ("Undo", undol, "Restore document."),
         new MenuItem (null, null, null),
         new MenuItem ("Copy", copyl, "Copy text to clipboard."),
         new MenuItem ("Cut", cutl, "Cut text to clipboard."),
         new MenuItem ("Paste", pastel, "Paste text from clipboard.")
      };

      // Install all of the previous menu items on the Edit menu.

      for (int i = 0; i < itemsEdit.length; i++)
      {
           if (itemsEdit [i].label == null)
           {
               menuEdit.addSeparator ();
               continue;
           }

           JMenuItem mi = new JMenuItem (itemsEdit [i].label);
           mi.addActionListener (itemsEdit [i].al);
           mi.setActionCommand (itemsEdit [i].desc);
           mi.addMouseListener (statusl);
           menuEdit.add (mi);
      }

      // Add the edit menu to the menu bar.

      mb.add (menuEdit);

      // Install StatBar's menu bar.

      setJMenuBar (mb);

      // Create a status bar for displaying help text associated with the menus
      // and their items.

      status = new JLabel (defaultStatusText);
      status.setBorder (BorderFactory.createEtchedBorder ());

      // Add the status bar to the bottom of the application's contentpane.
      getContentPane ().add (status, BorderLayout.SOUTH);

      // Establish a suitable initial size for displaying a document.

      setSize (450, 300);

      // Display GUI and start GUI processing.

      setVisible (true);
   }

   // Application entry point.

   public static void main (String [] args)
   {
      // Create the application's GUI and start the application.

      new StatBar ("StatBar");
   }
}DE>

在使用默认状态栏文字创建了 DE>statusDE> DE>JLabelDE> 之后,DE>StatBarDE> 刻画了一个边框 (通过 DE>status.setBorder (BorderFactory.createEtchedBorder ());DE>)以使状态栏标签与 GUI 的其他部分区别开来。然后,标签被添加到框架窗口内容窗格的南侧区域,这是状态栏的常见位置。

 

注意
如果不希望状态栏显示任何默认文字,则需要至少输入一个空格来代替文字。如果使用空字符串 (DE>“”DE>) 来代替,则不会显示状态栏(虽然会显示其边框)。必须处理状态栏标签的首选字体大小,因为这涉及到当前的字体和那个不可缺少的字符。如果状态栏标签不包含任何字符(空字符串),其首选字体大小就会是 0,造成状态栏无法显示。

 

DE>MenuItemListenerDE> 接口描述了一个 “File” 和 “Edit” 菜单的侦听程序。此接口的 DE>public void menuSelected(MenuEvent e)DE> 方法将在选择这些菜单时被调用,然后会显示菜单的帮助文字。选中一个菜单时,调用 DE>public void menuDeselected(MenuEvent e)DE> 显示文字。

DE>MouseListenerDE> 接口描述每个菜单项的侦听程序。其 DE>public void mouseEntered(MouseEvent e)DE> 在鼠标进入一个菜单项时被调用。然后,菜单项的帮助文字会显示在状态栏中。鼠标指针移动到菜单项外时,调用 DE>public void mouseExited(MouseEvent e)DE>,然后显示默认文字。

每个侦听程序都依赖于 DE>javax.swing.JMenuDE> 或 DE>javax.swing.JMenuItemDE> 的继承 DE>public void setActionCommand(String command)DE> 方法,此方法曾在指定每个菜单或菜单项的状态栏文字时被调用。文字是在侦听程序内部进行检索的,方法是调用相关的 DE>public String getActionCommand()DE> 方法。