Java 如何在我的MVC模式程序中更新我的表
我已经创建了一个使用MVC架构版本的程序。代码的目的是刮取网页列表的h1标题,并将结果返回到JTable 到目前为止,我的程序运行良好。它会按照我的要求返回结果,但直到最后才会更新表。我希望它在收到结果时更新表。我想这样做的方式,考虑到最佳实践原则,因为我只是在学习 我想,如果我想更新它,我必须对我的代码进行一些修改。我不确定动态更新GUI的最佳方法(线程、观察者、其他什么?)。我甚至不确定“在我的MVC模式中,这段代码应该放在哪里?”这个问题是否有意义 无论如何,我的观点是:Java 如何在我的MVC模式程序中更新我的表,java,multithreading,model-view-controller,user-interface,observer-pattern,Java,Multithreading,Model View Controller,User Interface,Observer Pattern,我已经创建了一个使用MVC架构版本的程序。代码的目的是刮取网页列表的h1标题,并将结果返回到JTable 到目前为止,我的程序运行良好。它会按照我的要求返回结果,但直到最后才会更新表。我希望它在收到结果时更新表。我想这样做的方式,考虑到最佳实践原则,因为我只是在学习 我想,如果我想更新它,我必须对我的代码进行一些修改。我不确定动态更新GUI的最佳方法(线程、观察者、其他什么?)。我甚至不确定“在我的MVC模式中,这段代码应该放在哪里?”这个问题是否有意义 无论如何,我的观点是: public c
public class SearchView extends JFrame{
//Components
private JLabel selectElementLabel = new JLabel("Element Selector:");
private JTextField selectElement = new JTextField("h1");;
private JComboBox<String> selectLocale;
private DefaultTableModel tableModel = new DefaultTableModel();
private JTable resultTable = new JTable(tableModel);
private JLabel statusLabel;
private JButton runButton = new JButton("Run");
private JButton clearButton = new JButton("Clear");
private SearchModel s_model;
//Constructor
public SearchView(SearchModel model) {
//Set the Logic here(model)
s_model = model;
//Initialise Components here(model)
selectLocale = new JComboBox<>(s_model.getLocales());
selectLocale.setSelectedIndex(13);
//Layout Components
JPanel userInputPanel = new JPanel();
userInputPanel.setLayout(new BoxLayout(userInputPanel, BoxLayout.X_AXIS));
userInputPanel.add(selectElementLabel);
userInputPanel.add(selectElement);
userInputPanel.add(selectLocale);
tableModel.addColumn("Page");
tableModel.addColumn("Data");
resultTable.setFillsViewportHeight(true);
JScrollPane resultScroller = new JScrollPane(resultTable);
resultScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
resultScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
resultScroller.setAlignmentX(Component.LEFT_ALIGNMENT);
JPanel controlButtons = new JPanel();
controlButtons.setLayout(new FlowLayout(FlowLayout.RIGHT));
controlButtons.add(statusLabel = new JLabel(s_model.getState()));
controlButtons.add(clearButton);
controlButtons.add(runButton);
this.setTitle("Element Searcher");
this.add(BorderLayout.NORTH, userInputPanel);
this.add(BorderLayout.CENTER, resultScroller);
this.add(BorderLayout.SOUTH, controlButtons);
this.setExtendedState(Frame.MAXIMIZED_BOTH);
this.setMinimumSize(new Dimension(900, 600));
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
void reset(){
tableModel.setRowCount(0);
}
String getSelectedElement(){
return selectElement.getText();
}
String getSelectedLocale(){
return selectLocale.getSelectedItem().toString();
}
void setResults(Object[] result){
tableModel.addRow(result);
}
void addRunListener(ActionListener run){
runButton.addActionListener(run);
}
void addClearListerner(ActionListener clear){
clearButton.addActionListener(clear);
}
}
最后是我的模型:
public class SearchModel {
//Constants
private static final String[] localeStrings = { "cs-cz", "da-dk", "de-at", "de-ch", "de-de", "el-gr", "en-ae", "en-au", "en-ca", "en-gb", "en-ie", "en-in", "en-nz", "en-us", "en-za", "es-cl", "es-co", "es-es", "es-mx", "fi-fi", "fr-be", "fr-ca", "fr-ch", "fr-fr", "hu-hu", "it-it", "ja-jp", "ko-kr", "nb-no", "nl-be", "nl-nl", "pl-pl", "pt-br", "pt-pt", "ru-ru", "sk-sk", "sv-se", "zh-hk", "zh-sg", "zh-tw" };
private static final String INITIAL_STATE = "idle";
private HashSet<String> pageList;
private Object[] scrapeResult;
private String locale = "en-us";
//Search State
private String searchState;
public SearchModel() {
reset();
}
public void setPageList(String loc){
locale = loc;
ScrapeXML scraper = new ScrapeXML(locale);
pageList = scraper.getUrls();
}
public void setResults(String page){
ScrapeElements scraper = new ScrapeElements(page, locale);
scrapeResult = scraper.getResults();
}
public void reset(){
searchState = INITIAL_STATE;
}
public String[] getLocales(){
return localeStrings;
}
public String getState(){
return searchState;
}
public HashSet<String> getPageList(){
return pageList;
}
public Object[] getResults(String page){
setResults(page);
return scrapeResult;
}
}
公共类搜索模型{
//常数
私有静态最终字符串[]localeStrings={“cs cz”、“da dk”、“de at”、“de ch”、“de de”、“el gr”、“en ae”、“en au”、“en ca”、“en gb”、“en ie”、“en in”、“en nz”、“en us”、“en za”、“es cl”、“es co”、“es es es es”、“es mx”、“fi fi”、“fr be”、“fr ca”、“fr ch”、“fr fr fr”、“hu hu-hu”、“it”、“ja jp”、“ko kr”、“nb no”、“nl be”、“nl-be”、“nl-nl-nl-nl-nl”、“pl”、“pl-pl”、“pl”、“pt-br”,“pt pt”、“ru ru”、“sk sk”、“sv se”、“zh hk”、“zh sg”、“zh tw”};
私有静态最终字符串INITIAL_STATE=“idle”;
私有HashSet页面列表;
私有对象[]结果;
私有字符串locale=“en-us”;
//搜索状态
私有字符串搜索状态;
公共搜索模型(){
重置();
}
公共无效设置页面列表(字符串位置){
地点=loc;
ScrapeXML scraper=新的ScrapeXML(区域设置);
pageList=scraper.getUrls();
}
公共void setResults(字符串页){
scrapelements scraper=新的scrapelements(页面、区域设置);
scrapresult=scraper.getResults();
}
公共无效重置(){
searchState=初始状态;
}
公共字符串[]getLocales(){
返回本地资源;
}
公共字符串getState(){
返回搜索状态;
}
公共HashSet getPageList(){
返回页面列表;
}
公共对象[]获取结果(字符串页){
设置结果(第页);
返回结果;
}
}
如果您对代码本身有任何意见或建议,请让我知道
谢谢!如果没有双重分派(也称为侦听器模式),就无法真正实现MVC。在模型中,您需要添加
public void addListener(ModelListener listener) {
}
并且(因此,当“您的”模型发生变化时,您可以停止收听旧模型)
因此,您可以让几乎未知的对象添加自己以接收模型更新,这些更新通常通过“侦听器”接口传递
public interface ModelListener {
public void modelChanged(ModelChangeEvent event);
}
其中ModelChangeEvent通常类似于
public class ModelChangeEvent {
private Model source;
public ModelChangeEvent(Model source, <possibly other fields here>) {
this.source = source;
}
public Model getSource() {
return source;
}
}
监听器在变更处理程序中的行为有一定的灵活性,最重要的是灵活性包含在监听器的范围内,模型现在基本上可以忽略正在听的内容,只关注谁在听
... in the model class ...
private void notifyListeners() {
ModelChangeEvent event = new ModelChangeEvent(this);
for (ModelListener listener : listeners) {
listener.modelChanged(event);
}
}
同样,在通知侦听器的方式上有很大的灵活性,但关键是当模型中可观察的“元素”发生变化时,所有侦听对象都应该接收一个调用。它通常放在私有方法中的原因是,这样可以更容易地重用模型,就像这样
... in the model class ...
public void setName(String name) {
this.name = name;
notifyListeners();
}
在每个听力课堂上,它可能(也可能不)读出名称,这取决于它显示的内容
... in a name and age sensitive listening class ...
public void modelChanged(ModelChangeEvent event) {
if (event.getSource() == model) {
name = model.getName();
age = model.getAge();
}
}
... in a name insensitive listening class ...
public void modelChanged(ModelChangeEvent event) {
if (event.getSource() == model) {
// this is my model!
age = model.getAge();
}
}
一旦你有了这样的东西,你的视图应该听你的模型,这样控制器就不必对你的模型的可观察的“元素”进行某种轮询,并定期刷新视图的感兴趣的表示项
是的,这意味着您的视图通常在您的模型中有数据的副本,这是一件好事;因为,如果您决定需要修改演示文稿,您就有一份数据副本要修改。在“名称显示”视图中,您可能希望强制执行一致的大小写,如下所示
... in a name and age sensitive listening class ...
... note that _name_ is the name in the _view_ not the model! ...
public void modelChanged(ModelChangeEvent event) {
if (event.getSource() == model) {
name = capitalize(model.getName());
age = model.getAge();
}
}
现在,您的模型和视图将始终保持同步。这大大简化了控制器代码,现在它只需要处理命令。对于每个命令,它可能会执行以下一项(或多项)操作:
setModel(model)
方法如果没有双重分派(也称为侦听器模式),就无法真正实现MVC
public void addListener(ModelListener listener) {
}
并且(因此,当“您的”模型发生变化时,您可以停止收听旧模型)
因此,您可以让几乎未知的对象添加自己以接收模型更新,这些更新通常通过“侦听器”接口传递
public interface ModelListener {
public void modelChanged(ModelChangeEvent event);
}
其中ModelChangeEvent通常类似于
public class ModelChangeEvent {
private Model source;
public ModelChangeEvent(Model source, <possibly other fields here>) {
this.source = source;
}
public Model getSource() {
return source;
}
}
监听器在变更处理程序中的行为有一定的灵活性,最重要的是灵活性包含在监听器的范围内,模型现在基本上可以忽略正在听的内容,只关注谁在听
... in the model class ...
private void notifyListeners() {
ModelChangeEvent event = new ModelChangeEvent(this);
for (ModelListener listener : listeners) {
listener.modelChanged(event);
}
}
同样,在通知侦听器的方式上有很大的灵活性,但关键是当模型中可观察的“元素”发生变化时,所有侦听对象都应该接收一个调用。它通常放在私有方法中的原因是,这样可以更容易地重用模型,就像这样
... in the model class ...
public void setName(String name) {
this.name = name;
notifyListeners();
}
在每个听力课堂上,它可能(也可能不)读出名称,这取决于它显示的内容
... in a name and age sensitive listening class ...
public void modelChanged(ModelChangeEvent event) {
if (event.getSource() == model) {
name = model.getName();
age = model.getAge();
}
}
... in a name insensitive listening class ...
public void modelChanged(ModelChangeEvent event) {
if (event.getSource() == model) {
// this is my model!
age = model.getAge();
}
}
一旦你有了这样的东西,你的视图应该听你的模型,这样控制器就不必对你的模型的可观察的“元素”进行某种轮询,并定期刷新视图的感兴趣的表示项
是的,这意味着您的视图通常具有数据的副本