解决日期选择问题,一劳永逸(使用Decorator模式实现日期选择组件)(五)

12/23/2007来源:J2EE/J2ME教程人气:6219

弹出对话框

   对话框包装器有2个类组成:第一个是Popup_dialog,继承与JDialog实现小的如前所示的框架。
生成常规对话框的主要难点是所有的装饰(框架与标题栏)的消失问题,因此如果你需要生成自己的标题栏,下面提供了具体实现的大部分代码:
标题栏是包含一个显示标题的标签和一个按钮(关闭按钮,如导航箭头一样加载图象资源)。
代码如下:
public class Popup_dialog extends JDialog
{
    PRivate Color TITLE_BAR_COLOR = com.holub.ui.Colors.LIGHT_YELLOW;
    private Color CLOSE_BOX_COLOR = com.holub.ui.Colors.DARK_RED;

    private JLabel title = new JLabel("xxxxxxxxxxxxxx");
    {   title.setHorizontalAlignment(SwingConstants.CENTER);
        title.setOpaque( false );
        title.setFont( title.getFont().deriveFont(Font.BOLD) );
    }

    private JPanel header = new JPanel();
    {   header.setBackground( TITLE_BAR_COLOR );
        header.setLayout( new BorderLayout() );
        header.setBorder( BorderFactory.createEmptyBorder(2,2,2,2) );
        header.add( title                   , BorderLayout.CENTER );
        header.add( create_close_button()   , BorderLayout.EAST   );
    }

    private JPanel content_pane = new JPanel();
    {   content_pane.setLayout( new BorderLayout() );
    }

    public Popup_dialog( Frame owner ){ super(owner); setModal(true); }
    public Popup_dialog( Dialog owner){ super(owner); setModal(true); }

    /* Code common to all constrUCtors. */
    {
        init_dragable();

        setUndecorated( true );
        JPanel contents = new JPanel();
        contents.setBorder( BorderFactory.createLineBorder(Color.BLACK,1) );
        contents.setLayout(new BorderLayout());
        contents.add(header,       BorderLayout.NORTH);
        contents.add(content_pane, BorderLayout.CENTER);
        contents.setBackground( Color.WHITE );

        setContentPane( contents ); // , BorderLayout.CENTER );
        setLocation(100,100);
    }

    private JButton create_close_button()
    {
        URL image = getClass().getClassLoader().getResource(
                                                "images/8px.red.X.gif");

        JButton b = (image!=null) ? new JButton( new ImageIcon(image) )
                                  : new JButton( "  X  " )
                                  ;

        Border outer = BorderFactory.createLineBorder(CLOSE_BOX_COLOR,1);
        Border inner = BorderFactory.createEmptyBorder(2,2,2,2);

        b.setBorder( BorderFactory.createCompoundBorder(outer,inner) );

        b.setOpaque( false );
        b.addActionListener
        (   new ActionListener()
            {   public void actionPerformed(ActionEvent e)
                {   Popup_dialog.this.setVisible(false);
                    Popup_dialog.this.dispose();
                }
            }
        );

        b.setFocusable( false );
        return b;
    }

    /** Set the dialog title to the indicated text. */
    public void setTitle( String text ){ title.setText( text ); }

    /** Add your widgets to the window returned by this method, in
     *  a manner similar to a JFrame. Do not modify the Popup_dialog
     *  itself. The returned container is a {@link JPanel JPanel}
     *  with a preinstalled {@link BorderLayout}.
     *  By default, it's colored dialog-box gray.
     *  @return the content pane.
     */

    public Container getContentPane(){ return content_pane; }
有点意思实现拖拉功能的代码。主要的思路是设置2个监听器。扑捉按纽的鼠标监听器与使用变量reference_position存储鼠标点击在标题标签中的上层窗口位置。
  当鼠标移动时,鼠标移动监听器将被激活。问题是和拖动联系一起的事件一般和屏幕联系在一起而不是和上层窗口联系一起。拉句柄在当前鼠标位置移动对话框到原始的reference_position位置。就象从原始位置拖动窗体到现在鼠标位置一样。
下面是代码:
private Point reference_position = new Point(0,0);
private MouseMotionListener movement_handler;
private MouseListener click_handler;

private void init_dragable()
{
    movement_handler =
        new MouseMotionAdapter()
        {   public void mouseDragged( MouseEvent e )
            {   // The reference position is the (window-relative)
                // cursor position when the click occurred. The
                // current_mouse_position is mouse position
                // now, and the deltas represent the distance
                // moved.

                Point current_mouse_position  = e.getPoint();
                Point current_window_location = getLocation();

                int delta_x=current_mouse_position.x - reference_position.x;
                int delta_y=current_mouse_position.y - reference_position.y;

                // Move the window over by the computed delta. This move
                // effectively shifts the window-relative, current mouse
                // position back to the original reference position.

                current_window_location.translate(delta_x, delta_y);
                setLocation(current_window_location);
            }
        };

    click_handler =
        new MouseAdapter()
        {   public void mousePressed( MouseEvent e )
            {   reference_position = e.getPoint();  // Start of the drag
            }
        };

    setDragable(true);
}

/** Turn dragability on or off.
*/
public void setDragable( boolean on )
{   if( on )
    {   title.addMouseMotionListener ( movement_handler );
        title.addMouseListener       ( click_handler    );
    }
    else
    {   title.removeMouseMotionListener ( movement_handler );
        title.removeMouseListener       ( click_handler );
    }
}


日期对话框

   剩下的都是些微不足道的包装。Date_selector_dialog 修饰者把包装的Date_selector放在弹出框中。它从Titled_date_selector中复制了一些代码用来显示对话框标题栏(当前日期和月份)这里根本就没有考虑导航条问题。因为Date_selector已经包含了导航条等等。。
public class Date_selector_dialog extends Popup_dialog implements Date_selector
{
    private Date_selector selector = new Date_selector_panel();

    /** Creates a dialog box with the indicated parent that holds
     *  a standard {@link Date_selector_panel Date_selector_panel}
     *  (as created using the no-arg constructor).
     */
    public Date_selector_dialog( Frame parent )
    {   super(parent);
        selector = new Navigable_date_selector( new Date_selector_panel() );
        init();
    }

    /* Like {@link #Date_selector_dialog(Frame),
     * but for a {@link Dialog} parent.
     */
    public Date_selector_dialog( Dialog parent )
    {   super(parent);
        selector = new Navigable_date_selector( new Date_selector_panel() );
        init();
    }

    /** Creates a dialog box with the indicated parent that holds
     *  the indicated Date_selector.
     *  Note that the current month and year display in the
     *  dialog-box title bar, so there's no need to display them in
     *  the selector too.
     */
    public Date_selector_dialog( Frame parent, Date_selector to_wrap )
    {   super(parent);
        selector = to_wrap;
        init();
    }

    /* Like {@link #Date_selector_dialog(Frame,Date_selector),
     * but for a {@link Dialog} parent.
     */

    public Date_selector_dialog( Dialog parent, Date_selector to_wrap )
    {   super(parent);
        selector = to_wrap;
        init();
    }

    /** Code common to all constructors.
     */
    private void init()
    {   getContentPane().add( (Container)selector, BorderLayout.CENTER );
        selector.addActionListener
        (   new ActionListener()
            {   public void actionPerformed( ActionEvent event )
                {   if( event.getID() == Date_selector.CHANGE_ACTION )
                    {   setTitle( event.getActionCommand() );
                    }
                    else
                    {   setVisible(false);
                        dispose();
                    }
                }
            }
        );
        ((Container)selector).setVisible(true);
        pack();
    }

    /** For use when you pop up a dialog using
     * setVisible(true) rather than {@link #select}.
     * @return the selected date or null if the dialog was closed
     *          without selecting anything.
     */
    public Date get_selected_date()
    {   return selector.get_selected_date();
    }

    /** Get the current date. The dialog stays in existence
     *  until the user closes it or selects a date, so this
     *  method can be used to see what month the user has
     *  scrolled to.
     *  @return the date currently displayed on the calendar.
     */
    public Date get_current_date()
    {   return selector.get_current_date();
    }

    /** Add an action listener for both.
     *  {@link Date_selector#CHANGE_ACTION} and
     *  {@link Date_selector#SELECT_ACTION} action events.
     */
    public void addActionListener(ActionListener l)
    {   selector.addActionListener(l);
    }

    /** Remove a previously added listener. */
    public void removeActionListener(ActionListener l)
    {   selector.removeActionListener(l);
    }

    /** Pops up the chooser and blocks until the user selects
     *  a date.
     * @return the selected date or null if the dialog was closed
     *          without selecting anything.
     */
    public Date select()
    {
        setVisible(true);
        return selector.get_selected_date();
    }

    public void roll(int f, boolean up) {        selector.roll(f,up);       }
    public int  get(int f)              { return selector.get(f);           }
}


decorator 模式:
总的来说,尽管日历选择组件比实用,但是这篇文章的重点还是使用细化实现的Decorator 模式的使用。Decorator 模式适合代码独立化,易于管理,实现和调试。每个Decorator 在逻辑上都很重要且规范化。你要改变某物,只要知道要改变的地方;你要添加一个装饰,只要继承一个的简单接口。
这种细化也减少了类中耦合度(不受欢迎的相互之间的依赖性)。在单一接口中改变或增加某一特性时使用Decorator 模式更能显示它的灵活性。so ,使用Decorator 模式吧

ealy ,java 爱好者,Matrix jsp翻译小组成员,可以点击http://www.matrix.org.cn/user_view.asp?username=ealy查看她的个人信息 
进入讨论组讨论。

(出处:http://www.knowsky.com)