I have a jDialog which contains some fields that need to get focused.
I am seeing some strange behaviour where sometimes the focusing fails, and if you hit the tab key you can see the focus change in the underlying parent window below, so clearly the focus did not get transferred.
I read the interesting article (by camickr) on focusing: http://tips4java.wordpress.com/2010/03/14/dialog-focus/ but that did not solve the problem.
Using that listener though, I was easily able to add debugging to try and see what is occurring...
public class RequestFocusListener implements AncestorListener
{
private boolean removeListener;
protected static org.slf4j.Logger logger = LoggerFactory.getLogger(RequestFocusListener.class);
/*
* Convenience constructor. The listener is only used once and then it is
* removed from the component.
*/
public RequestFocusListener() {
this(true);
}
/*
* Constructor that controls whether this listen can be used once or
* multiple times.
*
* @param removeListener when true this listener is only invoked once
* otherwise it can be invoked multiple times.
*/
public RequestFocusListener(boolean removeListener) {
logger.debug("creating RequestFocusListener, removeListener = " + removeListener);
this.removeListener = removeListener;
}
@Override
public void ancestorAdded(AncestorEvent e)
{
logger.debug("ancestorAdded detected");
JComponent component = e.getComponent();
logger.debug("requesting focus");
boolean success = component.requestFocusInWindow();
logger.debug("request focus in window result was: " + success);
if (!success) {
logger.debug("KeyboardFocusManager says focus failed.\nfocus owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
logger.debug("displayable="+component.isDisplayable());
logger.debug("lightweight="+component.isLightweight());
logger.debug("enabled="+component.isEnabled());
logger.debug("focusable="+component.isFocusable());
logger.debug("showing="+component.isShowing());
logger.debug("isRequestFocusEnabled="+component.isRequestFocusEnabled());
} else {
logger.debug("KeyboardFocusManager says we got focus. focus owner is " + KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
}
if (removeListener) {
component.removeAncestorListener( this );
}
}
@Override
public void ancestorMoved(AncestorEvent e) {
}
@Override
public void ancestorRemoved(AncestorEvent e) {
}
}
Then I added the listener to a component in the main panel of the JDialog
radioButton.addAncestorListener(new RequestFocusAncestorListener());
The output I am getting shows:
displayable=true
lightweight=true
enabled=true
focusable=true
showing=true
isRequestFocusEnabled=true
Stepping through the code to see what is making the request fail, I see it stops in Component.requestFocusHelper on:
boolean success = peer.requestFocus(this, temporary, focusedWindowChangeAllowed, time, cause);
I have read that the component must be displayable/visible/focusable) but the debugging shows that is ok.
Can anyone shed any light on what else might cause the requestFocus to fail? (and leave the focus in the calling parent panel, in this case in a jtable)
Sorry in advance for not providing a complete SSCCE, I have tried to reproduce this in a standalone example and can't get it to fail consistently.
I appreciate any thoughts/tips.
followup -
It seems like the first time I open the dialog, it gets focus, and then when I close and reopen the dialog the focus does not always get set.
Interestingly, after closing the dialog, if I change focus in the parent before opening the dialog again, the focus seems to always get set.
There are any number of possible reasons a request for focus will fail.
To start with, the Java Docs for Component#requestFocus
actually states
Because the focus behavior of this method is platform-dependent, developers are strongly encouraged to use requestFocusInWindow when possible.
And
This component must be displayable, focusable, visible and all of its ancestors (with the exception of the top-level Window) must be visible for the request to be granted
In order for a component to become focusable, the component and all it's ancestors must be valid (displayable). One of the common mistakes I see a lot is people using requestFocus
or requestFocusInWindow
when they create a new window, but before that window could actually be shown on the screen (setVisible
does not guarantee that the window will be immediately visible, only that some time in the future it will become visible).
The best approach in this situation is to use a WindowListener
and monitor for windowOpened
event. Event then, I'd be tempted to use a SwingUtilities#invokeLater
to make sure the window is actually displayable on the screen.
The other issue is relying on isDisplayable
. This method may return true
even when the window that the component is on is not (displaying on the screen).
A component becomes displayable when it is connected to a native screen resource. This can happen when it's ancestor window is either packed or made visible ... In fact I've found it very difficult to determine exactly when this might occur.
Update
I should also add that requestFocus
is just that, a "request". It is possible that the focus management sub system may veto the request because another field has refused to surrender focus (such as when the fields InputVerifier#shouldYieldFocus
returns false
)