setVisibility与focusChange的关系,附源码

2019-07-22  本文已影响0人  码农翻身记

问题回顾

今天特然收到一个bug的反馈,是在比较特殊操作路径导致。
界面逻辑:
A输入框监控焦点变化,如果失去焦点,则判断是否有输入内容,有内容则发送请求;

 mEt.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    if (mEt.getText().length() > 0) {
                        sendRequest();
                    }
                }
            }
        });

B是切换选项的spinner,某些选择会隐藏A输入框:

mEt.setVisibility(View.GONE);
mEt.setText("");

出现bug的路径:
焦点在A输入框上,然后修改B的选项,刚好会隐藏A,以为清空了A的内容,即使失去焦点,也不会发请求。奇怪的是竟然发送了请求,参数是A清空前的值。但在B的该选项情况下,不能带上A输入的参数,结果报错了。

回顾看代码突然醒悟,setVisibility可能会触发焦点变化的回调,例如当前控件由可见改为不可见,而焦点刚好在当前控件,就会执行焦点变化回调。(其实很容易理解,只是开发时没有注意,犯了低级错误!!!)
调整下代码顺序问题就解决了:

mEt.setText("");
mEt.setVisibility(View.GONE);

源码分析

public void setVisibility(@Visibility int visibility) {
      setFlags(visibility, VISIBILITY_MASK);
}

void setFlags(int flags, int mask) {
...
  /* 修改为GONE*/
  /* Check if the GONE bit has changed */
        if ((changed & GONE) != 0) {
            needGlobalAttributesUpdate(false);
            requestLayout();//需要重新布局

            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
                if (hasFocus()) clearFocus();//如果有焦点,会清除焦点
                clearAccessibilityFocus();
                destroyDrawingCache();
                if (mParent instanceof View) {
                    // GONE views noop invalidation, so invalidate the parent
                    ((View) mParent).invalidate(true);//父布局重绘
                }
                // Mark the view drawn to ensure that it gets invalidated properly the next
                // time it is visible and gets invalidated
                mPrivateFlags |= PFLAG_DRAWN;
            }
            if (mAttachInfo != null) {
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }
}

//清除焦点
public void clearFocus() {
      if (DBG) {
            System.out.println(this + " clearFocus()");
       }
       clearFocusInternal(null, true, true);
 }
 
  void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
        if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
            mPrivateFlags &= ~PFLAG_FOCUSED;

            if (propagate && mParent != null) {
                mParent.clearChildFocus(this);
            }

            onFocusChanged(false, 0, null);//触发焦点变化
            refreshDrawableState();

            if (propagate && (!refocus || !rootViewRequestFocus())) {
                notifyGlobalFocusCleared(this);
            }
        }
}

protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction,
            @Nullable Rect previouslyFocusedRect) {
       ...//省略其他代码
       //执行焦点变化回调
        if (li != null && li.mOnFocusChangeListener != null) {
            li.mOnFocusChangeListener.onFocusChange(this, gainFocus);
        }
        ...
}

总结

调用view.setVisibility可能会触发焦点变化回调,开发需要留意。

上一篇下一篇

猜你喜欢

热点阅读