Solution to the problem of invalid call of clearFocus method in Android application

  • 2021-07-13 06:13:47
  • OfStack

clearFocus is invalid?

The display effect of EditText is different when focus and non-focus: the cursor flashes when focus, and we usually set selector for it, and add a border to it when focus.

Usually, when we touch View other than EditText, we need to clear the focus of EditText. It is natural to think of EditText. clearFocus (), but it often doesn't work. (EditText. isFocus () is still true, and the cursor is still jumping...)

Implementation of clearFocus

The call stack of clearFocus (the important part):


View.clearFocus() ->
  View.clearFocusInternal() ->
  {
    1. mParent.clearChildFocus(this);//  From the View1 Traverse the parent node straight up , Know DecorView, The role is to put parent(ViewGroup) Stored in the mFocus Set to null, Clear the focus 
    2. rootViewRequestFocus();//  Call DecorView Adj. requestFocus() Method , The function is to find the 1 A View, And set it to focus 
  }

As can be seen from the call stack listed above, clearing focus actually involves two parts:

Clear the focus flag of the current View and clear the mFocus information stored in its ancestor node
Call the requestFocus () method of DecorView, re-search for 1 View and set it to focus
Implementation of requestFocus ()

requestFocus (int) supports four parameters, FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT and FOCUS_RIGHT, to indicate the flow direction of focus, but in fact, the incoming direction parameters have no effect. (This is actually better understood. In terms of FOCUS_RIGHT, should we choose View of the right sub-tree species or View drawn on the right? )

No matter what parameters are passed, requestFocus () traverses in advance to find the View of the first focusInTouchMode and set it as the focus.
The way to set it is:

Give the current View focus flag (mPrivateFlags)
Calling mParent. requestChildFocus () assigns itself to mFocus of its parent View, which then calls mParent. requestChildFocus () 1 until DecorView.
In this way, starting from DecorView, you can find the View of the real focus according to mFocus


@Override
public View findFocus() {
  if (DBG) {
    System.out.println("Find focus in " + this + ": flags="
        + isFocused() + ", child=" + mFocused);
  }

  if (isFocused()) {
    return this;
  }

  if (mFocused != null) {
    return mFocused.findFocus();
  }
  return null;
}

Note: According to the requestFocus search strategy, given a starting point, the View will always be the same, that is, if you call DecorView. requestFocus () many times, the focus will be the same, if the view level and focusable are not changed. Therefore, when you want a specific View to get the focus, you should directly call its requestFocus () method.

tips: For ViewGroup, you can choose whether parent or child gets focus first through the setting of descendantFocusability. Optional values: FOCUS_BEFORE_DESCENDANTS (default), FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS.

Is clearFocus really ineffective?

Of course not. The reason why EditText. clearFocus () is sometimes found to be invalid is that after clearing focus, View of focusInTouchMode will be found in the order of prior traversal, and it will be set to focus, and your EditText happens to be the first eligible View. (Therefore, it is not that it was not cleared successfully, but that it was set again after clearing! ! )

After knowing the reason, the solution is very simple. Find an View before EditText and set it to get the focus


View.setFocusableInTouchMode(true)
android:focusableInTouchMode="true"

If you don't know how to find an View before EditText, you can choose its parent (xxxLayout) directly, because the default policy of ViewGroup is: FOCUS_BEFORE_DESCENDANTS

Determine whether focus

isFocused (), which determines if it has focus
hasFocus (), which determines whether it or its own child has a common focus


Related articles: