| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2437 人关注过本帖
标题:自己编写的一个Windows风格按钮控件
只看楼主 加入收藏
ygp_sfec
Rank: 3Rank: 3
等 级:论坛游侠
威 望:2
帖 子:87
专家分:115
注 册:2009-9-8
结帖率:100%
收藏
 问题点数:0 回复次数:5 
自己编写的一个Windows风格按钮控件
     下面是我自己编写的一个Window风格的按钮控件,已经基本上完成,在对话框上所有的风格和动作都与标准Windows按钮无异,唯一的遗憾是我无法在控件内完成快捷键事件触发ActionEvent事件来响应快捷键,当然快捷键事件可以在父窗口触发,但是不能把快捷键事件封装在按钮内,则违反了面向对象的程序设计原则。另外对于不了解这个按钮内部实现的用户来说,使用也不方便,也不可能在父窗口内实现响应KeyEvent来响应按钮的快捷键,所以还得在控件内部实现,我尝试过在构造函数内添加父窗口的侦听器(用getParent().addKeyListener()方法),代码中红色的注释掉的一行,结果运行时抛出NullPointerException异常,不知道为什么,是否在按钮构造时父窗口尚未构造完成,如果这个方法不能使用,布置有什么方法可以实现这一目的。
异常的全部信息如下:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at MenuTestUI.CButton.<init>(CButton.java:58)
    at MenuTestUI.CFileDialog.<init>(CFileDialog.java:33)
    at MenuTestUI.CMenuTestFrame.OnFileOpen(CMenuTestFrame.java:160)
    at MenuTestUI.CMenuTestFrame.actionPerformed(CMenuTestFrame.java:117)
    at java.awt.MenuItem.processActionEvent(Unknown Source)
    at java.awt.MenuItem.processEvent(Unknown Source)
    at java.awt.MenuComponent.dispatchEventImpl(Unknown Source)
    at java.awt.MenuComponent.dispatchEvent(Unknown Source)


// 按钮空间的源代码

package MenuTestUI;

import java.awt.*;

import java.awt.event.*;



import java.util.Set;

public class CButton extends Component implements MouseListener,FocusListener,KeyListener{

    /**
     *
     */
    private static final long serialVersionUID = 1959233040601280332L;
   
    private static int        m_nCountDefault            = 0;
    private static CButton    m_buttonDefault            = null;
    private String            m_strLabel                 = new String();
    private boolean            m_bIsMouseInside         = false;
    private boolean            m_bIsMousePressed        = false;
    private boolean            m_bIsDefaultButton        = false;
    private ActionListener    m_listenerAction        = null;
    //private Container        m_windowParent;
   
    // 构造函数
   
    /** 构造一个空的按钮控件
     *
     */
    public CButton(){
        super();
        
        m_strLabel = "";
        addMouseListener(this);
        setFocusable(true);
        
        //getParent().addKeyListener(this);
   
    }
    /** 构造一个带有标签字符串的按钮
     *
     * 参数:
     * @param label:String字符串,用于在按钮上显示的文本。
     *
     * 返回值:本方法无返回值。
     */
    public CButton(String label){
        super();
        
        m_strLabel = label;
        addMouseListener(this);
        setFocusable(true);
        addFocusListener(this);
        
        addKeyListener(this);
        //getParent().addKeyListener(this);
        
    }
   
    /** 构造方法
     * CButton(String label,boolean bIsDefault);
     *
     * 参数:
     * @param label        :String类型的字符串,其值为按钮上显示的文本。
     * @param bIsDefault:布尔型,若其值为true,则表示该按钮为默认按钮,否则为非默认按钮。
     *                          按钮为默认按钮时,其黑框内绘制一道蓝色粗框, 若在容器中键入回车键,即可触发该按钮事件。
     *                           在同一个容器中只允许出现一个默认按钮,若用户代码中设置了多个默认按钮,则最 后一个被设置
     *                           的按钮为默认按钮。
     *
     * 返回值:本方法无返回值。
     */
    public CButton(String label,boolean bIsDefault){
        super();
        
        m_strLabel = label;
        SetAsDefault(bIsDefault);
        addMouseListener(this);
        setFocusable(true);
        addFocusListener(this);   
        getParent().addKeyListener(this);
    }
   
    /** void paint(Graphics g)
     * 绘制按钮,子类可通过覆盖本方法实现自绘按钮控件。
     *
     * 参数:
     *         g:用于绘制按钮的图形上下文。
     *
     * 返回值:本方法无返回值。
     */
    public void paint(Graphics g){

        DrawMyButton(g);
        DrawMyString(g);
        
        // 在按钮上绘制标签文字
        
        
    }
   
    // 绘制按钮标签文字,包括热键的下划线
    private void DrawMyString(Graphics g){
        g.setColor(this.getForeground());            // 将图形上下文的绘制颜色设置为当前按钮的前景色
        //g.setFont(new Font("宋体",Font.PLAIN,30));
        FontMetrics ft = g.getFontMetrics();        // 获取按钮的当前字体规格
        int nIndexOfShortcut = m_strLabel.indexOf('&');
        int nWidth = 0,nHeight = ft.getHeight(),nCenterY = getHeight()/2,nCenterX = getWidth()/2;
        if(nIndexOfShortcut != -1){
            String str = m_strLabel.substring(0, nIndexOfShortcut)+m_strLabel.substring(nIndexOfShortcut+1);
            nWidth = ft.charsWidth(str.toCharArray(), 0, str.length());
            int nStartX = nCenterX-nWidth/2;
            g.drawString(str, nStartX, nCenterY+nHeight/4);
            int nLineStartX = nStartX + ft.charsWidth(str.substring(0, nIndexOfShortcut).toCharArray(), 0, nIndexOfShortcut);
            int nLineEndX   = nStartX + ft.charsWidth(str.substring(0, nIndexOfShortcut+1).toCharArray(), 0, nIndexOfShortcut+1);
            g.drawLine(nLineStartX,nCenterY+nHeight*3/10,nLineEndX,nCenterY+nHeight*3/10);    // 绘制下划线。
        }
        else {
            nWidth = ft.charsWidth(m_strLabel.toCharArray(), 0, m_strLabel.length());
            g.drawString(m_strLabel, nCenterX-nWidth/2, nCenterY+nHeight/4);
            
        }
    }
   
    // 绘制按钮本身的图案,并根据不同的鼠标情况绘制不同的图案
    private void DrawMyButton(Graphics g){
        Color colorBackGround = this.getBackground();                // 取得按钮的当前背景颜色
        
        // 设置当前绘制颜色为黑色并绘制按钮边框
        g.setColor(new Color(0,45,255));                        
        g.drawRoundRect(0, 0, getWidth()-1, getHeight()-1,
                        getHeight()>getWidth()?getWidth()/4:getHeight()/4,
                        getHeight()>getWidth()?getWidth()/4:getHeight()/4);
        // 在按钮边框以不同颜色深度绘制线条以呈现立体效果
        int red = colorBackGround.getRed();
        int green = colorBackGround.getGreen();
        int blue = colorBackGround.getBlue();
        //System.out.println("The RGB of background color is R = "+red+", G = "+green+", B = "+blue);
        // 绘制左边与顶部的高亮边
        g.setColor(new Color(255,255,255));
        g.drawLine(2, 2, 2, getHeight()-2);
        g.drawLine(2, 2, getWidth()-2,2);
        g.setColor(new Color(red+5,green+5,blue+5));
        g.drawLine(3, 3, 3, getHeight()-3);
        g.drawLine(3, 3, getWidth()-3,3);
        
        // 绘制底部与右边的深色边
        g.setColor(new Color(red-10,green-10,blue-10));
        g.drawLine(getWidth()-2, 2, getWidth()-2, getHeight()-3);
        g.drawLine(2,getHeight()-2,getWidth()-3,getHeight()-2);
        g.setColor(new Color(red-5,green-5,blue-5));
        g.drawLine(getWidth()-3, 3, getWidth()-3, getHeight()-4);
        g.drawLine(3,getHeight()-3,getWidth()-4,getHeight()-3);        
        
        // 鼠标指针位于按钮之内时的图形
        if(m_bIsMouseInside && !m_bIsMousePressed){
            g.setColor(new Color(241,206,7));
            g.drawRoundRect(1, 1, getWidth()-4, getHeight()-4,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1);
            g.drawRoundRect(2, 2, getWidth()-5, getHeight()-5,
                            getHeight()>getWidth()?getWidth()/4-2:getHeight()/4-2,
                            getHeight()>getWidth()?getWidth()/4-2:getHeight()/4-2);   
            g.drawRoundRect(2, 2, getWidth()-6, getHeight()-6,
                            getHeight()>getWidth()?getWidth()/4-3:getHeight()/4-3,
                            getHeight()>getWidth()?getWidth()/4-3:getHeight()/4-3);   
            //DispFocus(g);
            }

        // 按钮处于焦点时的情形
        if(isFocusOwner())
            DispFocus(g);
        
        if(m_bIsDefaultButton && !m_bIsMousePressed && !m_bIsMouseInside){
            g.setColor(new Color(92,152,202));
            g.drawRoundRect(1, 1, getWidth()-3, getHeight()-3,
                            getHeight()>getWidth()?getWidth()/4:getHeight()/4,
                            getHeight()>getWidth()?getWidth()/4:getHeight()/4);
            g.drawRoundRect(2, 2, getWidth()-4, getHeight()-4,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1);   
            g.drawRoundRect(2, 2, getWidth()-5, getHeight()-5,
                            getHeight()>getWidth()?getWidth()/4-2:getHeight()/4-2,
                            getHeight()>getWidth()?getWidth()/4-2:getHeight()/4-2);
            
            }
        
        // 鼠标处于按下时的情形
        if(m_bIsMousePressed){
            
            // 用深灰色背景填充按钮区域
            g.setColor(new Color(red-10,green-10,blue -10));
            g.fillRoundRect(1, 1, getWidth()-2, getHeight()-2,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1,
                            getHeight()>getWidth()?getWidth()/4-1:getHeight()/4-1);
            
            // 绘制高亮底边和深色上边呈现立体效果
            // 绘制深色上边和左边
            g.setColor(new Color(red-20,green-20,blue-20));
            g.drawLine(3, 1, getWidth()-3, 1);
            g.drawLine(1, 3, 1, getHeight()-3);
            g.setColor(new Color(red-10,green-10,blue-10));
            g.drawLine(4, 2, getWidth()-4, 2);
            g.drawLine(2, 4, 2, getHeight()-4);
            
            // 绘制高亮底边和右边
            g.setColor(new Color(255,255,255));
            g.drawLine(3, getHeight()-2, getWidth()-3, getHeight()-2);
            g.drawLine(getWidth()-2, 3, getWidth()-2, getHeight()-3);
            g.setColor(new Color(235,235,235));
            g.drawLine(4, getHeight()-3, getWidth()-4, getHeight()-3);
            g.drawLine(getWidth()-3, 4, getWidth()-3, getHeight()-4);
            
            DispFocus(g);
            

        }
    }

    private void DispFocus(Graphics g){
        // 绘制虚线以显示焦点
        int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        int nStep = 2;
        
        // 绘制横线
        Color color = g.getColor();    // 保存当前绘图上下文的绘图颜色
        
        g.setColor(new Color(0,0,0));                // 设置当前绘图颜色为黑色
        x1 = x2 = 5;
        y1 = 3;
        y2 = getHeight()-4;
        for(int i = 0; i < (getWidth()-9)/(nStep+1); i++){
            x2 += 1;
            if(x2 > getWidth()-5)
                x2 = getWidth()-5;
            g.drawLine(x1, y1, x2, y1);
            g.drawLine(x1, y2, x2, y2);
            x2 += nStep;
            x1 = x2;
        }
        
        // 绘制竖虚线
        y1 = y2 = 5;
        x1 = 3;
        x2 = getWidth()-4;
        for(int i = 0; i < (getHeight()-9)/(nStep+1); i++){
            y2 += 1;
            if(y2 > getHeight()-5)
                y2 = getHeight()-5;
            g.drawLine(x1, y1, x1, y2);
            g.drawLine(x2, y1, x2, y2);
            y2 += nStep;
            y1 = y2;
        }   
        g.setColor(color);            // 将绘图颜色设置为还原为原来的绘图颜色
    }
   
    public String toString(){
        return getClass().getName() + "[CButton" + ((Container)getParent()).getComponentZOrder(this)
               + "@" + Integer.toHexString(this.hashCode()) + ", "+getX() + ", " + getY() + ", " + getWidth()
               + "x" + getHeight() + ","+ "label=" + m_strLabel + "]";
    }
   
    public String getLabel(){
        return m_strLabel;
    }
   
    public void setLabel(String label){
        m_strLabel = label;
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
        if(arg0.getButton() != MouseEvent.BUTTON1)
            return;
        // TODO Auto-generated method stub
        m_listenerAction.actionPerformed(new ActionEvent(this,
                                         ActionEvent.ACTION_PERFORMED,"CountDown"));
        //System.out.println("确定按钮");
        
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
        // TODO Auto-generated method stub
        m_bIsMouseInside    = true;
        repaint();
        
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
        // TODO Auto-generated method stub
        m_bIsMouseInside    = false;
        repaint();
        
    }

    @Override
    public void mousePressed(MouseEvent arg0) {
        // TODO Auto-generated method stub
        if(arg0.getButton() != MouseEvent.BUTTON1)
            return;
        m_bIsMousePressed = true;
        m_bIsDefaultButton = true;
        requestFocus();
        
        // 将其他按钮控件的m_bIsDefaultButtons属性设置为false(非默认按钮)
        /*Component com[] = getParent().getComponents();
        for(int i = 0; i<com.length;i++){
            if((com[i].getClass()).toString().contains("CButton")){
                ((CButton)com[i]).SetAsDefault(false);
            }
        }*/
        
        // 设置本控件为默认按钮
        //SetAsDefault(true);
        repaint();
        
    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
        // TODO Auto-generated method stub
        m_bIsMousePressed = false;
        
        repaint();
        
    }
    @Override
    public void focusGained(FocusEvent arg0) {
        // TODO Auto-generated method stub
        //arg0.
        m_bIsDefaultButton = true;
        repaint();
        
    }
    @Override
    public void focusLost(FocusEvent arg0) {
        // TODO Auto-generated method stub
        m_bIsDefaultButton = false;
        repaint();
        
    }
   
    /** void SetAsDefault(boolean bIsDefault)
     *            设置/取消按钮控件的默认属性,按钮为默认按钮时,其黑框内绘制一道蓝色粗框, 若在容器中键入回车键,即可触发
     * 该按钮事件。
     * 在同一个容器中只允许出现一个默认按钮,若用户代码中设置了多个默认按钮,则最 后一个被设置的按钮为默认按钮。
     * 参数:
     * @param bIsDefault:如为true,则设置为默认,否则为非默认
     *
     * 返回值:本方法无返回值
     */
    public void SetAsDefault(boolean bIsDefault){
        // 将当前按钮设置为默认按钮   
        m_bIsDefaultButton = bIsDefault;
        
        // 若bIsDefault为true,则将上一个默认按钮设置为非默认按钮,并将m_buttonDefault设置为当前按钮
        if(bIsDefault){
            if(m_buttonDefault != null && m_buttonDefault != this)
                m_buttonDefault.SetAsDefault(false);
            m_buttonDefault = this;
            
        }
        
        repaint();
    }
   
    public boolean IsDefault(){
        return m_bIsDefaultButton;
    }
   
    public void addActionListener(ActionListener listenerNew){
        m_listenerAction = AWTEventMulticaster.add(m_listenerAction, listenerNew);
    }
   
    public void removeActionListener(ActionListener listenerOld){
        m_listenerAction = AWTEventMulticaster.remove(m_listenerAction,listenerOld);
    }
    @Override
    public void keyPressed(KeyEvent arg0) {
        // TODO Auto-generated method stub
        
    }
    @Override
    public void keyReleased(KeyEvent arg0) {
        // TODO Auto-generated method stub
        
    }
    @Override
    public void keyTyped(KeyEvent arg0) {
        // TODO Auto-generated method stub
        //System.out.print("Parent Key Event");
        
    }
}
搜索更多相关主题的帖子: 编写 Windows 风格 按钮 控件 
2009-09-15 15:01
freish
Rank: 6Rank: 6
等 级:贵宾
威 望:23
帖 子:1223
专家分:437
注 册:2007-6-1
收藏
得分:0 
用一句
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
不就OK了么?

[url=http://shop63425653./]/cvbnm/a6/1d/f4/7dd1720119cf3b1dcfb570b467b24051.jpg" border="0" />[/url]
2009-09-15 16:01
ygp_sfec
Rank: 3Rank: 3
等 级:论坛游侠
威 望:2
帖 子:87
专家分:115
注 册:2009-9-8
收藏
得分:0 
嗯,试试看
2009-09-16 08:21
ygp_sfec
Rank: 3Rank: 3
等 级:论坛游侠
威 望:2
帖 子:87
专家分:115
注 册:2009-9-8
收藏
得分:0 
我是在学习java,编这个控件权当练习而已
2009-09-16 11:44
ygp_sfec
Rank: 3Rank: 3
等 级:论坛游侠
威 望:2
帖 子:87
专家分:115
注 册:2009-9-8
收藏
得分:0 
下一步目标是在按钮上增加图像
2009-09-16 13:18
ygp_sfec
Rank: 3Rank: 3
等 级:论坛游侠
威 望:2
帖 子:87
专家分:115
注 册:2009-9-8
收藏
得分:0 
此问题最终解决方法:
在按钮控件的paint()方法中增加如下语句:
        // 将父窗口所有组件的键盘输入事件加入侦听器  
        Component[] comp = getParent().getComponents();  
        if(comp != null){  
            for(int i = 0; i<comp.length;i++){  
                comp[i].addKeyListener(this);  
            }  
        }  
         
        // 将父窗口键盘输入事件加入到侦听器  
        getParent().addKeyListener(this);  
 
    然后让按钮类实现KeyListener接口,以侦听父窗口及其所有组件的键盘事件,并在按钮CButton类的KeyTyped()方法中进行过滤并触发ActionEvent事件通知父窗口处理,父窗口除了对ActionEvent事件进行处理外,无需编写任何代码,完美解决了上述问题。
小结:
    我原来将上述代码放在了CButton的构造函数中,此时父窗口尚未构造完成,所以执行时抛出NullPointerException异常,意思就是父窗口对象不存在,加入到paint()方法后,上述代码执行时,父窗口已构造完成,在内存中已有固定的内存区域与之对应,故不再抛出异常。
2009-09-20 22:07
快速回复:自己编写的一个Windows风格按钮控件
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.019418 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved