【问题标题】:Firing events from inner class which extends SwingWorker从扩展 SwingWorker 的内部类触发事件
【发布时间】:2013-06-11 12:05:32
【问题描述】:

我正在尝试从内部类触发事件,但它不起作用。这是我的代码:

抽象模型:

public abstract class AbstractModel {

    public PropertyChangeSupport propertyChangeSupport;

    public AbstractModel() {
        propertyChangeSupport = new PropertyChangeSupport(this);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    protected void firePropertyChange(String propertyName, Object oldValue,
            Object newValue) {
        propertyChangeSupport.firePropertyChange(propertyName, oldValue,
                newValue);
    }
}

型号:

public class GUImodel extends AbstractModel {

    // Variables
    private final ArrayList tempResultsTable = new ArrayList();
    private static boolean done;

    //
    // RUN PROGRAM
    //

    public ArrayList run(ArrayList iF) {

        try {
            final BackgroundThread myThread = new BackgroundThread();
            myThread.init(iF);
            myThread.execute();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return tempResultsTable;
    }

    public void done() {
        System.out.println("done() called");
        boolean oldValue = done;
        done = true;
        firePropertyChange(GUIcontroller.DONE_PROPERTY, oldValue, done);
    }


    class BackgroundThread extends SwingWorker<Void, Void> {

        private ArrayList inputsFilesDataList;

        public void init(ArrayList iF) {
            inputsFilesDataList = iF;
            done = false;
        }

        @Override
        public Void doInBackground() throws Exception {

            for (int i = 0; i < inputsFilesDataList.size(); i++) {
                System.out.println(i);
            }
            return null;
        }

        @Override
        protected void done() {
            try {
                boolean oldValue = done;
                done = true;
                firePropertyChange(GUIcontroller.DONE_PROPERTY, oldValue, done);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

查看:

public class GUIview{
   ...
   public void propertyChange(final PropertyChangeEvent event) {
        if (event.getPropertyName().equals(GUIcontroller.DONE_PROPERTY)) {
            String newTab = (String)event.getNewValue();
            updateTab(newTab);
        }
   }
   ...
}

抽象控制器:

public abstract class AbstractController implements PropertyChangeListener {

    public final ArrayList<AbstractFrame> registeredViews;
    public final ArrayList<AbstractModel> registeredModels;

public AbstractController() {
    registeredViews = new ArrayList();
    registeredModels = new ArrayList();
}

public void addModel(AbstractModel model) {
    registeredModels.add(model);
    model.addPropertyChangeListener(this);
}

public void removeModel(AbstractModel model) {
    registeredModels.remove(model);
    model.removePropertyChangeListener(this);
}

public void addView(GUIview view) {
    registeredViews.add(view);
}

public void removeView(AbstractFrame view) {
    registeredViews.remove(view);
}

@Override
public void propertyChange(PropertyChangeEvent event) {
    for (AbstractFrame view : registeredViews) {
        view.propertyChange(event);
        }
    }
}

控制器

public class GUIcontroller extends AbstractController {

public static final String DONE_PROPERTY = "done";
ArrayList inputsFilesList = m_model.loadFromExcel();    
    @Override
    public void propertyChange(PropertyChangeEvent event) {

        if (event.getPropertyName().equals(GUIcontroller.DONE_PROPERTY)) {          
            m_view.getResultsModel().updateResultsTableDataList(
                    m_model.getTempResultsTable());
        } else {
            for (AbstractFrame view : registeredViews) {
                view.propertyChange(event);
            }
        }
    }

     public runProgram(){
          m_model.run(inputsFilesList);
     }


}

主要。

public class GUImain {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    createGUI();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public static void createGUI() {

        InputsModel inputsModel = new InputsModel();
        ResultsModel resultsModel = new ResultsModel();

        GUImodel model = new GUImodel();

        GUIcontroller controller = new GUIcontroller();
        controller.addModel(model);

        GUIview view = new GUIview(controller, model, inputsModel, resultsModel);

        controller.addControllerListerners();

        view.setVisible(true);
    }
}

对这个问题有任何想法吗?

我需要在后台线程中运行一些方法,因此我使用了一个扩展 SwingWorker 的内部类。一旦该线程完成,我需要触发一个事件来向我的控制器报告一些更改。

done() 方法中的“firePropertyChange(...)”行没有被执行。

相关问题:如果某个类ClassA扩展了ClassAA,其内部类ClassB扩展了ClassBB,那么内部类ClassB是否也扩展了ClassAA?

【问题讨论】:

  • 鉴于目前提供的信息,我认为您的问题目前无法回答。我们无法评论未见过的代码,也无法测试您的代码或问题。例如,你怎么知道firePropXXX(...) 没有被执行?考虑创建并发布sscce。是的,这将需要您付出很多努力,将您的问题提炼到本质并配对不相关的代码,但它可能是值得的。

标签: java swing inner-classes swingworker propertychangelistener


【解决方案1】:

一个愚蠢的疯狂猜测:

您是否将侦听器添加到正确的 SwingPropertyChangeSupport 对象?这必须是 AbstractModel 持有的对象,而不是 BackgroundThread 持有的对象。换句话说,为了让您的侦听器能够接收到属性已更改的通知,他们必须将其 PropertyChangeListener 添加到 AbstractModel,并且您的 BackgroundThread 类必须具有执行此操作的方法。

编辑
要么从您的 AbstractModel 类中删除 PropertyChangeSupport,并且只使用由 mKorbel 建议的 SwingWorker 持有的那个。 1+ 他的回答。

否则,您的问题在代码/信息方面严重不足,无法在当前状态下提供真正知识渊博的答案,我们所能做的就是猜测可能的问题及其答案。请在此处提问时考虑我们的观点,问问自己需要哪些信息才能让某人完全理解并回答问题。


编辑 2

您的代码证明我的假设实际上是正确的,即您将 PropertyChangeListener 添加到错误的 PropertyChangeSupport 对象中,因此 SwingWorker 中的通知(从未将 PropertyChangeListener 分配给其支持)将无效在已添加到 AbstractModel 的支持对象的侦听器上。

这个:

firePropertyChange(GUIcontroller.DONE_PROPERTY, oldValue, done);

在 SwingWorker 的 SwingPropertyChangeSupport 对象上调用,而不是在 AbstractModel 的对象上调用。

一种可能的解决方案是将您的点火方法更改为:

GUImodel.this.firePropertyChange(GUIcontroller.DONE_PROPERTY, oldValue, done);

以便正确的支持对象通知正确的听众。


编辑 3
我的 SSCCE(比真正的 SSCCE 长一点)证明了我的论点:

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.event.SwingPropertyChangeSupport;

public class MvcSscce {
   public static void main(String[] args) {
      EventQueue.invokeLater(new Runnable() {
         public void run() {
            try {
               createGUI();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      });
   }

   public static void createGUI() {
      GUImodel model = new GUImodel();

      GUIcontroller controller = new GUIcontroller();
      controller.addModel(model);

      GUIview view = new GUIview(controller);

      view.setVisible(true);
   }
}

class GUIview {
   private JPanel mainPanel = new JPanel();
   private JFrame frame = new JFrame("Fubar");

   public GUIview(AbstractController controller) {
      mainPanel.add(new JButton(controller.getButtonAction()));
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationRelativeTo(null);
   }

   public void setVisible(boolean visible) {
      frame.setVisible(visible);
   }
}

abstract class AbstractModel {
   // note this should be a SwingPropertyChangeSupport
   public SwingPropertyChangeSupport propertyChangeSupport;

   public abstract void run();

   public AbstractModel() {
      propertyChangeSupport = new SwingPropertyChangeSupport(this);
   }

   public void addPropertyChangeListener(PropertyChangeListener listener) {
      propertyChangeSupport.addPropertyChangeListener(listener);
   }

   public void removePropertyChangeListener(PropertyChangeListener listener) {
      propertyChangeSupport.removePropertyChangeListener(listener);
   }

   protected void firePropertyChange(String propertyName, Object oldValue,
         Object newValue) {
      propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
   }
}

class GUImodel extends AbstractModel {
   private boolean done = false;

   public void run() {
      done = false;
      final BackgroundThread myThread = new BackgroundThread();
      myThread.execute();
   }

   private class BackgroundThread extends SwingWorker<Void, Void> {
      private static final long SLEEP_TIME = 2000;

      @Override
      protected Void doInBackground() throws Exception {
         Thread.sleep(SLEEP_TIME);
         return null;
      }

      @Override
      protected void done() {
         System.out.println("done() called");
         boolean oldValue = done;
         done = true;

         // fire both property change listeners and see what gets notified
         firePropertyChange(GUIcontroller.DONE_PROPERTY, oldValue, done);
         GUImodel.this.firePropertyChange(GUIcontroller.DONE_PROPERTY_2,
               oldValue, done);
      }
   }
}

class AbstractController implements PropertyChangeListener {

   private AbstractModel model;

   public void addModel(AbstractModel model) {
      this.model = model;
      model.addPropertyChangeListener(this);
   }

   public Action getButtonAction() {
      @SuppressWarnings("serial")
      Action buttonAction = new AbstractAction("Press Me") {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            model.run();
         }
      };
      return buttonAction;
   }

   @Override
   public void propertyChange(PropertyChangeEvent evt) {
      String output = String.format("Evt: %s, newValue: %s",
            evt.getPropertyName(), evt.getNewValue());
      System.out.println(output);
   }
}

class GUIcontroller extends AbstractController {

   public static final String DONE_PROPERTY_2 = "done property 2";
   public static final String DONE_PROPERTY = "done property";

}

请注意,在 2 秒延迟后,会通知侦听器,但只通知 DONE_PROPERTY_2 属性,而不是 DONE_PROPERTY。

【讨论】:

  • 不是你是对的,不错的收获,肯定是天堂和地狱之间的东西(地球)
  • 我的控制器正在监听模型的变化:model.addPropertyChangeListener(this); PropertyChangeSupport 对象在 AbstractModel 中声明,firePropertyChange() 方法与该对象相关。视图通过控制器接收通知。
  • @boblinwien:您的代码证明我的假设是正确的。请参阅编辑 2
  • @HovercraftFullOfEels,你的假设为我节省了好几个小时。非常感谢我灵魂的最深处:D
【解决方案2】:
  • 添加PropertyChangeListener to instance of SwingWorker,并没有为SwingWorker实现另一个Swing Listener

  • SwingWorkerPropertyChangeListener返回事件DONEPENDINGSTARTED

  • public void propertyChange(PropertyChangeEvent event) {,您可以将适当的通知分发到Model,也可以分发到View,因为done()process()publish()保证输出在@987654324上完成@

【讨论】:

  • 向 SwingWorker 的实例添加 PropertyChangeListener 是什么意思?该实例应该启动事件,而不是监听。 propertyChange() 侦听器已在控制器中声明。视图通过控制器而不是直接获取通知。
  • 您是否尝试过链接代码,尤其是在answer by @trashgod 中,有几种方法可以正确地做到这一点,例如你可以看我的another question, based on a few great posts (put together) by HFOE
  • AFAIU,我需要向模型添加一个侦听器以从 SwingWorker 实例获取通知,该实例在后台运行并启动自己的事件(完成、挂起、启动)。我会继续调查...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-10
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 2023-04-08
  • 1970-01-01
  • 2012-03-16
相关资源
最近更新 更多