美文网首页
setVisibility与focusChange的关系,附源码

setVisibility与focusChange的关系,附源码

作者: 码农翻身记 | 来源:发表于2019-07-22 16:56 被阅读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可能会触发焦点变化回调,开发需要留意。

    相关文章

      网友评论

          本文标题:setVisibility与focusChange的关系,附源码

          本文链接:https://www.haomeiwen.com/subject/kwiwlctx.html