How to Use the Focus SubsystemMany components even those primarily operated with the mouse, such as buttons can be operated with the keyboard. For a key press to affect a component, the component must have the keyboard focus. From the user's point of view, the component with the keyboard focus is generally prominent(突出) with a dotted or black border, for example. The window containing the component is also more prominent than other windows onscreen. These visual cues let the user know to which component any typing will relate. Only one component at a time in the window system can have the keyboard focus. Exactly how a window gains the focus depends on the windowing system. There is no foolproof way, across all platforms, to ensure that a window gains the focus. On some operating systems, such as Microsoft Windows, the front window usually becomes the focused window. In these cases, the A component generally gains the focus when the user clicks it, or when the user tabs between components, or otherwise interacts with a component. A component can also be given the focus programmatically, such as when its containing frame or dialog-box is made visible. This code snippet shows how to give a particular component the focus every time the window gains the focus: //Make textField get the focus whenever frame is activated. frame.addWindowFocusListener(new WindowAdapter() { public void windowGainedFocus(WindowEvent e) { textField.requestFocusInWindow(); } }); If you want to ensure that a particular component gains the focus the first time a window is activated, you can call the //...Where initialization occurs... JFrame frame = new JFrame("Test"); JPanel panel = new JPanel(new BorderLayout()); //...Create a variety of components here... //Create the component that will have the initial focus. JButton button = new JButton("I am first"); panel.add(button); frame.getContentPane().add(panel); //Add it to the panel frame.pack(); //Realize the components. //This button will have the initial focus. button.requestFocusInWindow(); frame.setVisible(true); //Display the window. Alternatively, you can apply a custom Version note: This section describes the focus architecture implemented in JDK 1.4. Prior to the 1.4 release, JComponent methods, such as setNextFocusableComponent , getNextFocusableComponent , requestDefaultFocus , and isManagingFocus , were used to manage the keyboard focus. These methods are now deprecated. Another method, requestFocus , is discouraged because it tries to give the focus to the component's window, which is not always possible. As of JDK 1.4, you should instead use the requestFocusInWindow method, which does not attempt to make the component's window focused. The method returns a boolean value indicating whether the method succeeded.
The rest of this section covers the following topics:
Introduction to the Focus SubsystemThe focus subsystem is designed to do the right thing as invisibly as possible. In most cases it behaves in a reasonable manner, and if it does not you can tweak(吱声,大气干扰) its behavior in various ways. Some common scenarios might include:
The Try this:
The JWindow and focus: To use a JWindow component in your GUI, you should know that the JWindow component's owning frame must be visible in order for any components in the window to get the focus. By default, if you do not specify an owning frame for a JWindow component, an invisible owning frame is created for it. The result is that components in the JWindow component might not be able to get the focus. The solution is either to specify a visible owning frame when creating the JWindow component, or to use an undecorated JFrame component instead.
A focus cycle (or focus traversal cycle) is a set of components that share a common ancestor in the containment hierarchy. The focus cycle root is the container that is the root for a particular focus traversal cycle. By default, every A focus traversal policy determines the order in which a group of components are navigated. Swing provides the In most Look and Feel models, components are navigated using the Tab and Shift-Tab keys. These keys are the default focus traversal keys and can be changed programmatically. For example, you can add Enter as a forward focus traversal key with the following four lines of code: Set forwardKeys = getFocusTraversalKeys( KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); Set newForwardKeys = new HashSet(forwardKeys); newForwardKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)); setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, newForwardKeys); Tab shifts the focus in the forward direction. Shift-Tab moves the focus in the backward direction. For example, in FocusConceptsDemo, the first button has the initial focus. Tabbing moves the focus through the buttons into the text area. Additional tabbing moves the cursor within the text area but not out of the text area because, inside a text area, Tab is not a focus traversal key. However, Control-Tab moves the focus out of the text area and into the first text field. Likewise, Control-Shift-Tab moves the focus out of the text area and into the previous component. The Control key is used by convention to move the focus out of any component that treats Tab in a special way, such as You have just received a brief introduction to the focus architecture. If you want more details, see the specification for the Focus Subsystem. Validating InputA common requirement of GUI design is a component that restricts the user's input 鈥? for example, a text field that allows only numeric input in decimal format (money, for example) or a text field that allows only 5 digits for a zip code. Release 1.4 provides a handy, easy-to-use formatted text field component that allows input to be restricted to a variety of localizable formats. You can also specify a custom formatter for the text field, which can perform special checking such as determining whether values are not only formatted correctly, but also reasonable. You can use an input verifier as an alternative to a custom formatter, or when you have a component that is not a text field. An input verifier allows you to reject specific values, such as a properly formatted but invalid zip code, or values outside of a desired range, for example a body temperature higher than 110°F. To use an input verifier, you create a subclass of A component's input verifier is consulted whenever the component is about to lose the focus. If the component's value is not acceptable, the input verifier can take appropriate action, such as refusing to yield the focus on the component or replacing the user's input with the last valid value and then allowing the focus to transfer to the next component. However, The following two examples show mortgage calculators. One uses formatted text fields and the other uses input verification with standard text fields. Try this:
You can find the code for the Input Verification demo in class MyVerifier extends InputVerifier implements ActionListener { double MIN_AMOUNT = 10000.0; double MAX_AMOUNT = 10000000.0; double MIN_RATE = 0.0; int MIN_PERIOD = 1; int MAX_PERIOD = 40; public boolean shouldYieldFocus(JComponent input) { boolean inputOK = verify(input); makeItPretty(input); updatePayment(); if (inputOK) { return true; } else { Toolkit.getDefaultToolkit().beep(); return false; } } protected void updatePayment() { double amount = DEFAULT_AMOUNT; double rate = DEFAULT_RATE; int numPeriods = DEFAULT_PERIOD; double payment = 0.0; //Parse the values. try { amount = moneyFormat.parse(amountField.getText()). doubleValue(); } catch (ParseException pe) {pe.printStackTrace();} try { rate = percentFormat.parse(rateField.getText()). doubleValue(); } catch (ParseException pe) {pe.printStackTrace();} try { numPeriods = decimalFormat.parse(numPeriodsField.getText()). intValue(); } catch (ParseException pe) {pe.printStackTrace();} //Calculate the result and update the GUI. payment = computePayment(amount, rate, numPeriods); paymentField.setText(paymentFormat.format(payment)); } //This method checks input, but should cause no side effects. public boolean verify(JComponent input) { return checkField(input, false); } protected void makeItPretty(JComponent input) { checkField(input, true); } protected boolean checkField(JComponent input, boolean changeIt) { if (input == amountField) { return checkAmountField(changeIt); } else if (input == rateField) { return checkRateField(changeIt); } else if (input == numPeriodsField) { return checkNumPeriodsField(changeIt); } else { return true; //should not happen } } //Checks that the amount field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method sets the //value to the minimum or maximum value if necessary and // (even if not) sets it to the parsed number so that it // looks good -- no letters, for example. protected boolean checkAmountField(boolean change) { boolean wasValid = true; double amount = DEFAULT_AMOUNT; //Parse the value. try { amount = moneyFormat.parse(amountField.getText()). doubleValue(); } catch (ParseException pe) { pe.printStackTrace(); wasValid = false; } //Value was invalid. if ((amount < MIN_AMOUNT) || (amount > MAX_AMOUNT)) { wasValid = false; if (change) { if (amount < MIN_AMOUNT) { amount = MIN_AMOUNT; } else { // amount is greater than MAX_AMOUNT amount = MAX_AMOUNT; } } } //Whether value was valid or not, format it nicely. if (change) { amountField.setText(moneyFormat.format(amount)); amountField.selectAll(); } return wasValid; } //Checks that the rate field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method reigns in the //value if necessary and (even if not) sets it to the //parsed number so that it looks good -- no letters, //for example. protected boolean checkRateField(boolean change) { ...//Similar to checkAmountField... } //Checks that the numPeriods field is valid. If it is valid, //it returns true; otherwise, returns false. If the //change argument is true, this method reigns in the //value if necessary and (even if not) sets it to the //parsed number so that it looks good -- no letters, //for example. protected boolean checkNumPeriodsField(boolean change) { ...//Similar to checkAmountField... } public void actionPerformed(ActionEvent e) { JTextField source = (JTextField)e.getSource(); shouldYieldFocus(source); //ignore return value source.selectAll(); } } Note that the The input verifier is installed using the private MyVerifier verifier = new MyVerifier(); ... amountField.setInputVerifier(verifier); Making a Custom Component FocusableFor a component to gain the focus, it must satisfy three requirements: it must be visible, enabled, and focusable. An input map may also be given. For more information about input map, read How to Use Key Bindings. The TrackFocusDemo example defines the simple component public Picture(Image image) { this.image = image; setFocusable(true); addMouseListener(this); addFocusListener(this); } The call to the To visually show changes in the focus (by drawing a red border only when the component has the focus), To gain the focus when the user clicks on the picture, the component has a mouse listener. The listener's public void mouseClicked(MouseEvent e) { //Since the user clicked on us, let us get focus! requestFocusInWindow(); } See Tracking Focus Changes to Multiple Components for more discussion of the TrackFocusDemo example. Customizing Focus TraversalThe focus subsystem determines a default order that is applied when using the focus traversal keys (such as Tab) to navigate. The policy of a Swing application is determined by The Try this:
You can find the demo's code in The check box was removed from the focus cycle with this line of code: togglePolicy.setFocusable(false); Here is the application's custom ... JTextField tf1 = new JTextField("Field 1"); JTextField tf2 = new JTextField("A Bigger Field 2"); JTextField tf3 = new JTextField("Field 3"); JTextField tf4 = new JTextField("A Bigger Field 4"); JTextField tf5 = new JTextField("Field 5"); JTextField tf6 = new JTextField("A Bigger Field 6"); JTable table = new JTable(4,3); ... public FocusTraversalDemo() { super(new BorderLayout()); JTextField tf1 = new JTextField("Field 1"); JTextField tf2 = new JTextField("A Bigger Field 2"); JTextField tf3 = new JTextField("Field 3"); JTextField tf4 = new JTextField("A Bigger Field 4"); JTextField tf5 = new JTextField("Field 5"); JTextField tf6 = new JTextField("A Bigger Field 6"); JTable table = new JTable(4,3); togglePolicy = new JCheckBox("Custom FocusTraversalPolicy"); togglePolicy.setActionCommand("toggle"); togglePolicy.addActionListener(this); togglePolicy.setFocusable(false); //Remove it from the focus cycle. //Note that HTML is allowed and will break this run of text //across two lines. label = new JLabel("<html>Use Tab (or Shift-Tab) to navigate from component to component.<p>Control-Tab (or Control-Shift-Tab) allows you to break out of the JTable.</html>"); JPanel leftTextPanel = new JPanel(new GridLayout(3,2)); leftTextPanel.add(tf1, BorderLayout.PAGE_START); leftTextPanel.add(tf3, BorderLayout.CENTER); leftTextPanel.add(tf5, BorderLayout.PAGE_END); leftTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5)); JPanel rightTextPanel = new JPanel(new GridLayout(3,2)); rightTextPanel.add(tf2, BorderLayout.PAGE_START); rightTextPanel.add(tf4, BorderLayout.CENTER); rightTextPanel.add(tf6, BorderLayout.PAGE_END); rightTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5)); JPanel tablePanel = new JPanel(new GridLayout(0,1)); tablePanel.add(table, BorderLayout.CENTER); tablePanel.setBorder(BorderFactory.createEtchedBorder()); JPanel bottomPanel = new JPanel(new GridLayout(2,1)); bottomPanel.add(togglePolicy, BorderLayout.PAGE_START); bottomPanel.add(label, BorderLayout.PAGE_END); add(leftTextPanel, BorderLayout.LINE_START); add(rightTextPanel, BorderLayout.CENTER); add(tablePanel, BorderLayout.LINE_END); add(bottomPanel, BorderLayout.PAGE_END); setBorder(BorderFactory.createEmptyBorder(20,20,20,20)); Vector<Component> order = new Vector<Component>(7); order.add(tf1); order.add(tf2); order.add(tf3); order.add(tf4); order.add(tf5); order.add(tf6); order.add(table); newPolicy = new MyOwnFocusTraversalPolicy(order); } To use a custom MyOwnFocusTraversalPolicy newPolicy = new MyOwnFocusTraversalPolicy(); frame.setFocusTraversalPolicy(newPolicy); You can remove the custom focus traversal policy by setting the Tracking Focus Changes to Multiple ComponentsIn some situations an application may need to track which component has the focus. This information might be used to dynamically update menus or perhaps a status bar. If you need to track the focus only on specific components, it may make sense to implement a focus event listener. If a focus listener is not appropriate, you can instead register a The following example demonstrates tracking the focus owner by installing a property change listener on the keyboard focus manager. Try this:
You can view the demo's code in KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); focusManager.addPropertyChangeListener( new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { String prop = e.getPropertyName(); if (("focusOwner".equals(prop)) && ((e.getNewValue()) instanceof Picture)) { Component comp = (Component)e.getNewValue(); String name = comp.getName(); Integer num = new Integer(name); int index = num.intValue(); if (index < 0 || index > comments.length) { index = 0; } info.setText(comments[index]); } } } ); The custom component, pic1 = new Picture(createImageIcon("images/" + mayaString + ".gif", mayaString).getImage()); pic1.setName("1"); Timing Focus TransfersFocus transfers are asynchronous. This quality can lead to some odd timing-related problems and assumptions, especially during automatic transfers of the focus. For example, imagine an application with a window containing a Start button, a Cancel button and a text field. The components are added in this order:
When the application is launched, the public void actionPerformed(ActionEvent e) { //This works. start.setEnabled(false); cancel.requestFocusInWindow(); } As desired, the focus goes from the Start button to the Cancel button, rather than to the text field. But a different result would occur if the same methods were called in the opposite order as follows: public void actionPerformed(ActionEvent e) { //This does not work. cancel.requestFocusInWindow(); start.setEnabled(false); } In this case, the focus is requested on the Cancel button before it has left the Start button. The call to the There are several situations in which you need to make focus requests after all other changes that might affect the focus applies to:
The Focus APIThe following tables list the commonly used constructors and methods related to focus. The focus API falls into four categories:
For more detailed information about the focus architecture, see the specification for the Focus Subsystem. You may also find How to Write a Focus Listener useful. All of this API was introduced in JDK 1.4.
Creating and Using a Custom FocusTraversalPolicy
All of this API was introduced in JDK 1.3.
KeyboardFocusManager Properties This table defines the bound properties for All of this API was introduced in JDK 1.4.
Examples that Use FocusThe following table lists examples that manipulate the focus:
|
|
来自: 勤奋不止 > 《界面库swing》