神秘的一次施法例外

时间:2009-10-13 19:56:15

标签: java

我正在尝试使用JComponent类的PropertyChangeSupport。 但是当我执行以下代码时,首先单击菜单按钮会给运行时强制执行Exception,但它总是运行良好。

FrameListener.java

import javax.swing.*;
import java.beans.*;
import java.awt.*;
import java.awt.event.*;

public class FrameListener extends JFrame implements ActionListener, PropertyChangeListener
{

    JLabel lblMessage;
    JMenuItem changeFont;
    FontSource fe = new FontSource(this,"Font Editor");

    public FrameListener(){

        super("Hello World");
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) ;
        increaseReadability() ;

        changeFont.addActionListener(this);

        fe.addPropertyChangeListener(this);

        setSize(400,200);
        setVisible(true);
    }

    private void increaseReadability(){
        JPanel panel = new JPanel();
        Font f = new Font("Times New Roman",Font.BOLD,24);
        lblMessage = new JLabel("HELLO WORLD",SwingConstants.CENTER);
        lblMessage.setFont(f);
        panel.add(lblMessage);


        JMenuBar actionBar = new JMenuBar();    
        JMenu edit = new JMenu("Edit");
        changeFont = new JMenuItem("Font");

        actionBar.add(edit);
        edit.add(changeFont);
        add(actionBar,BorderLayout.NORTH);
        add(panel,BorderLayout.CENTER);
    }

    public void propertyChange(PropertyChangeEvent pcevent){
        Object obj = pcevent.getNewValue() ;
        System.out.println(obj.getClass()) ;

        //Statement occuring problem 1st time
        Font newFt = (Font)obj;

        lblMessage.setFont(newFt);
    }

    public void actionPerformed(ActionEvent evt){
        fe.setVisible(true);
    }

    public static void main(String argv[]) {
        new FrameListener();
    }
}

FontSource.java

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.*;


public class FontSource extends JDialog implements ActionListener {

    private Font newFont = new Font("Times New Roman",Font.BOLD,12);

    JComboBox cbfType,cbfStyle,cbfSize;
    JButton btnOk,btnCancel;

    //protected PropertyChangeSupport changes = new PropertyChangeSupport(this);

    public Font getNewFont(){
        return newFont;
    }

    public void setNewFont(Font f){
        Font old = newFont;

        try{

            //this statement calls the propertyChange() of FrameListener
                        //if u are removing comments, replace the following statement with
                       // changes.firePropertyChange("Font Changed",old,f);
            firePropertyChange("Font Changed",old,f);

            newFont = f;
        }
        catch(Exception e){
            System.out.println(e);
        }
    }

    public FontSource(Frame fr,String title){

        super(fr,title);
        // getting font family from the graphics environment.
        GraphicsEnvironment gf = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String myfont[] = gf.getAvailableFontFamilyNames();
        cbfType = new JComboBox(myfont);
        add(cbfType);

        String fontStyle[] = {"PLAIN","ITALIC","BOLD",};
        cbfStyle = new JComboBox(fontStyle);
        add(cbfStyle);

        String fontSize[] = {"10","12","14","16","18","20","24","26","28","36","48","72"};
        cbfSize = new JComboBox(fontSize);
        add(cbfSize);

        btnOk =new JButton("OK");
        btnCancel =new JButton("Cancel");

        add(btnOk);
        add(btnCancel);

        // adding action listener
        btnOk.addActionListener(this);
        btnCancel.addActionListener(this);

        // setting layout and size for the dialog
        setLayout(new FlowLayout());
        setSize(170,170);
    }

    public void actionPerformed(ActionEvent ae){

        if(ae.getSource()==btnOk){
            String type = (String) cbfType.getSelectedItem();
            String style = (String)cbfStyle.getSelectedItem();
            int s = 0;
            int size = Integer.parseInt((String)cbfSize.getSelectedItem());
            if(style=="PLAIN")
                s= Font.PLAIN;
            else {
                if(style =="BOLD")
                    s= Font.BOLD;
                else
                    s= Font.ITALIC;
            }

            Font f = new Font(type,s,size);

            setNewFont(f);
        }
        else{
            this.setVisible(false);
        }
    }

    /*
    public void addPropertyChangeListener(PropertyChangeListener l){
        System.out.println("attachement done...");
        changes.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l){
        changes.removePropertyChangeListener(l);
    }
    */
}

但如果我使用自己的PropertyChangeSupport(删除FontSource.java中的注释),那么它的工作正常。 我尽我所能,但没有得到这个。 Thnx提前: - )

3 个答案:

答案 0 :(得分:3)

如果您实施PropertyListener,您将收到您注册的组件的所有属性更改。可以有许多类型,其值将由属性更改的类型确定。

Component setFont方法的实施将触发名称为"font"的属性更改。如果您测试该名称,您应该没问题:

public void propertyChange(PropertyChangeEvent pcevent){
    Object obj = pcevent.getNewValue() ;
    System.out.println(obj.getClass()) ;

    //Problem should not occur with this call.
    if (pcevent.getPropertyName().equals("font")){
       Font newFt = (Font)obj;

       lblMessage.setFont(newFt);
    }
}

答案 1 :(得分:1)

我的猜测......

属性更改侦听无法通过属性名称进行区分。由于您正在收听FontSource的所有属性,因此您无疑会看到不是Font的内容。

在propertyChange()中,您可以在事件中打印属性名称,以确保问题出现。

解决方案是仅注册您感兴趣的属性,或者检查propertyChange()方法中的属性名称。

答案 2 :(得分:0)

最后我明白了。

当你编译并执行以下文件时,会显示答案。

<强> FontSource.java

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.*;


public class FontSource extends JDialog{

    public FontSource(Frame fr,String title){

        super(fr,title);
        setSize(600, 400) ;
        JTextArea area = new JTextArea() ;
        area.setEditable(false) ;
        add(new JScrollPane(area)) ;

        String str = "If u look at the command prompt, then it is very clear," + "\n" +
                     "that, only on your first click on JMenuItem (font)," + "\n" +
                     "u get an output on cmd, for next future clicks on JMenuItem (font), u get no output on cmd." + "\n\n" +
                     "Reason : On first click, ActionListener attached to JMenuItem (font) invokes," + "\n" +
                     "fe.setVisible(true), which internally calls setBackground method of Component class only once." + "\n" +
                     "Now, setBackground method calls firePropertyChange(\"background\", oldValue, newValue),"  + "\n" +
                     "which in turn also gets executed once." + "\n\n" +
                     "Now, solution to this is clearly mentioned in the reply" + "\n" +
                     "provided by akf for my question asked on stackoverflow. cheers :-)" ;

        area.setText(str) ;
    }    
}

<强> FrameListener.java

import javax.swing.*;
import java.beans.*;
import java.awt.*;
import java.awt.event.*;

public class FrameListener extends JFrame implements ActionListener, PropertyChangeListener
{

    JLabel lblMessage;
    JMenuItem changeFont;
    FontSource fe = new FontSource(this,"Font Editor");

    public FrameListener(){

        super("Hello World");
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE) ;
        increaseReadability() ;

        changeFont.addActionListener(this);

        fe.addPropertyChangeListener(this);

        setSize(400,200);
        setVisible(true);
    }

    private void increaseReadability(){
        JMenuBar actionBar = new JMenuBar();    
        JMenu edit = new JMenu("Edit");
        changeFont = new JMenuItem("Font");
        actionBar.add(edit);
        edit.add(changeFont);
        add(actionBar,BorderLayout.NORTH);
    }

    public void propertyChange(PropertyChangeEvent pcevent){
        Object obj = pcevent.getNewValue() ;
        System.out.println(obj.getClass() + ", " + pcevent.getPropertyName()) ;
    }

    public void actionPerformed(ActionEvent evt){
        fe.setVisible(true);
    }

    public static void main(String argv[]) {
        new FrameListener();
    }
}