2012-12-12 34 views
2

我有一個方法,可以遞歸地啓用/禁用JPanel內的所有組件。也有例外列表。所以,我可以做以下JSpinner與其他鞦韆組件有什麼不同?

  • 禁用所有組件panel1除了textfield1, textfield3誰應該被啓用。
  • 啓用panel2中的所有組件,但應禁用button2, label3的應用程序除外。

這裏是一個SSCCE這正是這麼做的:

import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridLayout; 
import java.util.HashSet; 
import java.util.Set; 

import javax.swing.JButton; 
import javax.swing.JCheckBox; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JSpinner; 
import javax.swing.JTextField; 
import javax.swing.SpinnerNumberModel; 

public class Main { 
    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     final JPanel panel = new JPanel(new GridLayout(3, 3)); 

     final JTextField textfield = new JTextField("asdf"); 
     final JButton button = new JButton("asdf"); 
     final JCheckBox checkbox = new JCheckBox("asdf"); 
     final JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); 
     final JLabel label = new JLabel("asdf"); 

     panel.add(textfield); 
     panel.add(button); 
     panel.add(checkbox); 
     panel.add(spinner); 
     panel.add(label); 
     // fill in some random stuff 
     for (int i = 0; i < 4; i++) 
      panel.add(new JLabel("asdf")); 

     frame.setContentPane(panel); 
     frame.setSize(300, 100); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       boolean toggle = true; 
       while (true) { 
        toggle = !toggle; 
        Set<Component> enableList = new HashSet<Component>(); 
        Set<Component> disableList = new HashSet<Component>(); 
        enableList.add(textfield); 
        enableList.add(spinner); 
        disableList.add(checkbox); 
        setEnableAllRec(panel, toggle, disableList, enableList); 
        try { 
         Thread.sleep(1000); 
        } catch (InterruptedException e) {} 
       } 
      } 
     }).start(); 
     frame.setVisible(true); 
    } 

    public static void setEnableAllRec(Container root, boolean defaultState, Set<Component> disableList, Set<Component> enableList) { 
     if (root == null) return; 
     for (Component c : root.getComponents()) { 
      if (disableList != null && disableList.contains(c)) { 
       c.setEnabled(false); 
       disableList.remove(c); 
      } else if (enableList != null && enableList.contains(c)) { 
       c.setEnabled(true); 
       enableList.remove(c); 
      } else c.setEnabled(defaultState); 
      if (c instanceof Container) setEnableAllRec((Container) c, defaultState, disableList, enableList); 
     } 
    } 
} 

的SSCCE將所有組件每秒啓用/禁用交替。除了一些應始終啓用的組件以及一些始終應禁用的組件。除了JSpinner以外,這種方法非常有效。它被檢測爲一個組件,但在列表中找不到(因此不作爲特殊情況處理)。我嘗試過的所有其他揮杆組件都按預期工作。

你能告訴我JSpinner怎麼了?

我希望SSCCE是不是太混亂...;)

+1

奇。如果我在「setEnableAllRec」入口處添加一個顯示'disableList'和'enableList'中的項目數的打印語句,它會在多次調用後降爲零。而且這些電話的發生頻率比每秒一次更多。我想知道是否有一些併發問題在這裏發生。 –

+0

'setEnableAllRec'方法被多次調用,因爲它是遞歸的。但是你是對的,在線程調用完成後列表是空的。紀堯姆的回答實際上解釋了這一點。謝謝你的時間。 :) – brimborium

+0

啊,當然。很明顯,我的大腦並沒有運作得更早。 –

回答

3

僅僅因爲一個JSpinner的包含其他組件,如2個按鈕和一個編輯器(見javax.swing.plaf.basic.BasicSpinnerUI的代碼),你遞歸去所有容器,因此,也包含在JSpinner的包含組件中。

順便說一句,你的主題違反了Swing-EDT。你還是執行在Swing Timer

解決辦法很簡單:

import java.awt.Component; 
import java.awt.Container; 
import java.awt.GridLayout; 
import java.util.HashSet; 
import java.util.Set; 

import javax.swing.JButton; 
import javax.swing.JCheckBox; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JSpinner; 
import javax.swing.JTextField; 
import javax.swing.SpinnerNumberModel; 

public class Main { 
    public static void main(String[] args) { 
     JFrame frame = new JFrame(); 
     final JPanel panel = new JPanel(new GridLayout(3, 3)); 

     final JTextField textfield = new JTextField("asdf"); 
     final JButton button = new JButton("asdf"); 
     final JCheckBox checkbox = new JCheckBox("asdf"); 
     final JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); 
     final JLabel label = new JLabel("asdf"); 

     panel.add(textfield); 
     panel.add(button); 
     panel.add(checkbox); 
     panel.add(spinner); 
     panel.add(label); 
     // fill in some random stuff 
     for (int i = 0; i < 4; i++) { 
      panel.add(new JLabel("asdf")); 
     } 

     frame.setContentPane(panel); 
     frame.setSize(300, 100); 
     frame.setLocationRelativeTo(null); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       boolean toggle = true; 
       while (true) { 
        toggle = !toggle; 
        Set<Component> enableList = new HashSet<Component>(); 
        Set<Component> disableList = new HashSet<Component>(); 
        enableList.add(textfield); 
        enableList.add(spinner); 
        disableList.add(checkbox); 
        setEnableAllRec(panel, toggle, disableList, enableList); 
        try { 
         Thread.sleep(1000); 
        } catch (InterruptedException e) { 
        } 
       } 
      } 
     }).start(); 
     frame.setVisible(true); 
    } 

    public static void setEnableAllRec(Container root, boolean defaultState, Set<Component> disableList, Set<Component> enableList) { 
     if (root == null) { 
      return; 
     } 
     for (Component c : root.getComponents()) { 
      if (disableList != null && disableList.contains(c)) { 
       c.setEnabled(false); 
       disableList.remove(c); 
      } else if (enableList != null && enableList.contains(c)) { 
       c.setEnabled(true); 
       enableList.remove(c); 
      } else { 
       c.setEnabled(defaultState); 
       if (c instanceof Container) { 
        setEnableAllRec((Container) c, defaultState, disableList, enableList); 
       } 
      } 
     } 
    } 
} 
+0

哦,我沒有想到這一點。我不得不看兩次才能看到你的修復。我對這個解決方案非常滿意。 ;) 非常感謝你! – brimborium

+1

@brimborium沒問題。另外,我想你的線程只適用於demo/SSCCE,否則這應該放在'javax.swing.Timer'中。 –

+0

是的,該線程僅用於演示目的,在我的真實應用程序中,這將不時被調用(有時也基於用戶操作)。 – brimborium