自定义EditText修改Compound Drawable触发无限Focus Change Event问题求助
嘿,这个问题我之前做自定义输入控件时也踩过坑!核心原因就是你在OnFocusChangeListener里修改CompoundDrawable时,系统会触发额外的焦点变更回调——这是因为修改Drawable会触发View的刷新/重布局逻辑,间接导致焦点状态被误判,进而形成无限循环。下面给你几个靠谱的解决思路:
方案1:添加标志位拦截重复回调
这是最直接的解决办法,用一个布尔变量标记当前是否正在主动更新Drawable,避免处理因修改Drawable引发的虚假焦点变更。
public class ClearableEditText extends EditText { private boolean isUpdatingDrawable = false; private Drawable clearDrawable; public ClearableEditText(Context context) { super(context); init(); } // 别忘了实现其他构造方法(带AttributeSet、defStyleAttr的版本) private void init() { clearDrawable = getResources().getDrawable(R.drawable.ic_clear); clearDrawable.setBounds(0, 0, clearDrawable.getIntrinsicWidth(), clearDrawable.getIntrinsicHeight()); setOnFocusChangeListener((v, hasFocus) -> { // 如果正在更新Drawable,直接跳过本次回调 if (isUpdatingDrawable) { return; } updateClearIconVisibility(hasFocus); }); // 处理清除图标的点击逻辑 setOnTouchListener((v, event) -> { if (getCompoundDrawables()[2] != null) { boolean isClearIconClicked = event.getX() > (getWidth() - getPaddingRight() - clearDrawable.getIntrinsicWidth()); if (isClearIconClicked && event.getAction() == MotionEvent.ACTION_UP) { setText(""); return true; } } return false; }); } private void updateClearIconVisibility(boolean hasFocus) { isUpdatingDrawable = true; // 开启标记 if (hasFocus && !TextUtils.isEmpty(getText())) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], clearDrawable, getCompoundDrawables()[3]); } else { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], null, getCompoundDrawables()[3]); } isUpdatingDrawable = false; // 关闭标记 } }
这个标志位就像一个“临时开关”,告诉系统:现在是我主动修改Drawable,这时候触发的焦点变化是假的,不用处理,直接切断无限循环的链条。
方案2:仅在必要时更新Drawable
另一种思路是减少不必要的setCompoundDrawables调用——只有当当前Drawable状态和预期不符时,才去修改它,从根源上避免触发额外的焦点回调。
@Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { boolean shouldShowIcon = gainFocus && !TextUtils.isEmpty(getText()); Drawable currentRightDrawable = getCompoundDrawables()[2]; // 只有当状态需要变更时,才更新Drawable if (shouldShowIcon && currentRightDrawable != clearDrawable) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], clearDrawable, getCompoundDrawables()[3]); } else if (!shouldShowIcon && currentRightDrawable != null) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], null, getCompoundDrawables()[3]); } // 必须调用父类方法,保证正常的焦点逻辑 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); }
这里我们重写了View自身的onFocusChanged方法,先判断当前是否需要显示图标,再对比现有Drawable的状态,只有不一致时才执行修改操作,避免了无意义的刷新。
方案3:结合TextWatcher优化状态判断
因为图标显示既依赖焦点状态,也依赖文本内容,我们可以把焦点变化和文本变化的逻辑合并,进一步减少不必要的更新:
private void init() { clearDrawable = getResources().getDrawable(R.drawable.ic_clear); clearDrawable.setBounds(0, 0, clearDrawable.getIntrinsicWidth(), clearDrawable.getIntrinsicHeight()); // 监听文本变化 addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { updateClearIcon(isFocused()); } @Override public void afterTextChanged(Editable s) {} }); // 监听焦点变化 setOnFocusChangeListener((v, hasFocus) -> { updateClearIcon(hasFocus); }); } private void updateClearIcon(boolean hasFocus) { boolean shouldShow = hasFocus && !TextUtils.isEmpty(getText()); Drawable currentRight = getCompoundDrawables()[2]; if (shouldShow && currentRight != clearDrawable) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], clearDrawable, getCompoundDrawables()[3]); } else if (!shouldShow && currentRight != null) { setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], null, getCompoundDrawables()[3]); } }
这个方案把两种状态变化的逻辑统一到一个方法里,确保只有当真正需要变更图标时才执行修改,既解决了无限触发问题,也让代码更简洁。
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

