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可能会触发焦点变化回调,开发需要留意。