技术开发 频道

JDK 7探秘三:用JLayer装饰Swing组件

  其中createLayer()方法是最重要的代码,它创建了一个匿名LayerUI子类的实例,绘制了墙纸和JPanel视图,创建了UI,在实例中包含了UI的面板容器。

  绘制操作是由LayerUI's public void paint(Graphics g,JComponent c)方法实现的,第二个参数引用了视图(被装饰的组件)中的JLayer实例,不是引用的视图。

  在视图后创建了渐变渲染墙纸后,调用paint()方法确保视图(没有嵌套面板的单一面板)是透明的,它将会隐藏墙纸,然后绘制视图。

  paint()对比paintLayer()

  JDK文档中除了提到paint()方法外,还提到了paintLayer()方法,我这里之所以选择paint()方法,是因为LayerUI中不存在paintLayer(),此外,文档还错误地引用了paintLayer() doesn't exist in LayerUI. Furthermore,the documentation incorrectly refers to addPropertyChange(),configureGraphics(),processComponentEvent(),processFocusEvent(),processHierarchyBoundsEvent(),processHierarchyEvent(),processKeyEvent(),processMouseEvent(),processMouseMotionEvent(),processMouseWheelEvent(),and repaintLayer()这些在LayerUI中根本不存在的方法,当然这些方法也可能在JDK 7最终发布时会包含进来。

  图1显示了有墙纸背景的UI。

 JLayer自定义绘制

图 1 在用户界面后加了一层墙纸背景

  ReverseText程序演示了自定义绘制,避开了事件触发,不需要检测事件,因为程序只关心墙纸的绘制效果。相反,清单2显示了一个需要响应鼠标移动事件的程序代码。

  清单2. BrandedUI.java

// BrandedUI.java
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.plaf.LayerUI;
public class BrandedUI
{
  
private static Color PALE_BLUE = new Color (0.0f, 0.0f, 1.0f, 0.3f);
  
private static Color PALE_RED = new Color (1.0f, 0.0f, 0.0f, 0.3f);
  
private static Font BRAND_FONT = new Font ("Arial", Font.BOLD, 18);
  
private static String MSG = "My brand";
  
private static JLayer<JPanel> createLayer ()
   {
      LayerUI
<JPanel> layerUI;
      layerUI
= new LayerUI<JPanel> ()
      {
        
private Color color = PALE_BLUE;
        
public void installUI (JComponent c)
         {
            
super.installUI (c);
            ((JLayer) c).setLayerEventMask (AWTEvent.MOUSE_MOTION_EVENT_MASK);
         }
        
public void eventDispatched (AWTEvent e,
                                      JLayer
<? extends JPanel> l)
         {
            MouseEvent me
= (MouseEvent) e;
            Point pt
= SwingUtilities.convertPoint ((Component) me.getSource (),
                                                    me.getX (), me.getY (), l);
            
int cx = l.getWidth ()/2;
            
int cy = l.getHeight ()/2;
            
if (pt.x > cx-45 && pt.x < cx+45 && pt.y > cy-10 && pt.y < cy+10)
                color
= PALE_RED;
            
else
                color
= PALE_BLUE;
            l.repaint ();
         }
        
public void paint (Graphics g, JComponent c)
         {
            
// Paint the view.
            super.paint (g, c);
            
// Paint the brand.
            g.setColor (color);
            g.setFont (BRAND_FONT);
            
int width = g.getFontMetrics ().stringWidth (MSG);
            
int height = g.getFontMetrics ().getHeight ();
            g.drawString (MSG, (c.getWidth ()
-width)/2,
                          c.getHeight ()
/2+height/4);
         }
        
public void uninstallUI (JComponent c)
         {
            
super.uninstallUI (c);
            ((JLayer) c).setLayerEventMask (
0);
         }
      };
      
// Create a user interface to be decorated.
      JPanel pnlMain = new JPanel ();
      pnlMain.setLayout (
new GridLayout (2, 1));
      JPanel pnlTemp
= new JPanel ();
      JLabel lblName
= new JLabel ("Name:");
      pnlTemp.add (lblName);
      JTextField txtName
= new JTextField (20);
      pnlTemp.add (txtName);
      pnlMain.add (pnlTemp);
      pnlTemp
= new JPanel ();
      JLabel lblAddr
= new JLabel ("Address:");
      pnlTemp.add (lblAddr);
      JTextField txtAddr
= new JTextField (20);
      pnlTemp.add (txtAddr);
      pnlMain.add (pnlTemp);
      
// Create the layer for the main panel using our custom layerUI.
      return new JLayer<JPanel> (pnlMain, layerUI);
   }
  
private static void createAndShowUI ()
   {
      JFrame frame
= new JFrame ("Branded UI");
      frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
      frame.add (createLayer ());
      frame.pack ();
      frame.setLocationRelativeTo (
null);
      frame.setVisible (
true);
   }
  
public static void main (String [] args)
   {
      Runnable r
= new Runnable ()
                   {
                      
public void run ()
                       {
                          createAndShowUI ();
                       }
                   };
      EventQueue.invokeLater (r);
   }
}

  上面的代码渲染UI上的文本印记,我们通常使用印记提醒用户使用的是试用软件,印记文本是半透明的,以便背景可以全部显示,我们不希望这个印记给用户造成太大的干扰。

  另一方面,我们希望用户能注意到这个印记,让他们下定决心购买这款软件,清单2中的代码通过改变印记的颜色(改成淡红色)来达到这个目的,当鼠标移到初始值是绿色的印记面板上时,颜色就变成淡红色。 

0