我有一个自定义小部件,它使用setCompoundDrawablesWithIntrinsicBounds()
方法将清除按钮设置为右侧可绘制。
现在我需要向窗口小部件添加错误,并且我尝试使用默认功能并依赖setError()
方法。
我遇到的问题是,在我设置错误之后,即使我之后再次设置错误,我的右侧绘图也不会再次显示。
是否有人遇到此问题并可能找到问题的解决方案?
谢谢。
LE:
我忘了提及如果我使用setError("My error hint text", null)
代替setError("Please choose an valid destination!")
,一切正常,但您可以猜到错误图标不会显示。
LLE: 这是我的自定义类,它处理清除图标的显示及其动作。
/**
* Class of {@link android.widget.AutoCompleteTextView} that includes a clear (dismiss / close) button with a
* OnClearListener to handle the event of clicking the button
* <br/>
* Created by ionut on 26.04.2016.
*/
public class ClearableAutoCompleteTextView extends AppCompatAutoCompleteTextView implements View.OnTouchListener {
/**
* The time(in milliseconds) used for transitioning between the supported states.
*/
private static final int TRANSITION_TIME = 500;
/**
* Flag for hide transition.
*/
private static final int TRANSITION_HIDE = 101;
/**
* Flag for show transition.
*/
private static final int TRANSITION_SHOW = 102;
/**
* Image representing the clear button. (will always be set to the right of the view).
*/
@DrawableRes
private static int mImgClearButtonRes = R.drawable.ic_clear_dark;
/**
* Task with the role of showing the clear action button.
*/
private final Runnable showClearButtonRunnable = new Runnable() {
@Override
public void run() {
Message msg = transitionHandler.obtainMessage(TRANSITION_SHOW);
msg.sendToTarget();
}
};
/**
* Task with the role of hiding the clear action button.
*/
private final Runnable hideClearButtonRunnable = new Runnable() {
@Override
public void run() {
Message msg = transitionHandler.obtainMessage(TRANSITION_HIDE);
msg.sendToTarget();
}
};
/**
* Flag indicating if the default clear functionality should be disabled.
*/
private boolean mDisableDefaultFunc;
// The default clear listener which will clear the input of any text
private OnClearListener mDefaultClearListener = new OnClearListener() {
@Override
public void onClear() {
getEditableText().clear();
setText(null);
}
};
/**
* Touch listener which will receive any touch events that this view handles.
*/
private OnTouchListener mOnTouchListener;
/**
* Custom listener which will be notified when an clear event was made from the clear button.
*/
private OnClearListener onClearListener;
/**
* Transition drawable used when showing the clear action.
*/
private TransitionDrawable mTransitionClearButtonShow = null;
/**
* Transition drawable used when hiding the clear action.
*/
private TransitionDrawable mTransitionClearButtonHide = null;
private AtomicBoolean isTransitionInProgress = new AtomicBoolean(false);
private final Handler transitionHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TRANSITION_HIDE:
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
isTransitionInProgress.set(false);
break;
case TRANSITION_SHOW:
setCompoundDrawablesWithIntrinsicBounds(0, 0, mImgClearButtonRes, 0);
isTransitionInProgress.set(false);
break;
default:
super.handleMessage(msg);
}
}
};
/* Required methods, not used in this implementation */
public ClearableAutoCompleteTextView(Context context) {
super(context);
init();
}
/* Required methods, not used in this implementation */
public ClearableAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
/* Required methods, not used in this implementation */
public ClearableAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (getCompoundDrawables()[2] == null) {
// Pass the touch event to other listeners, if none, the normal flow is resumed
return null != mOnTouchListener && mOnTouchListener.onTouch(v, event);
}
// React only when the UP event is detected
if (event.getAction() != MotionEvent.ACTION_UP) {
return false;
}
// Detect the clear button area of touch
int x = (int) event.getX();
int y = (int) event.getY();
int left = getWidth() - getPaddingRight() - getCompoundDrawables()[2].getIntrinsicWidth();
int right = getWidth();
boolean tappedX = x >= left && x <= right && y >= 0 && y <= (getBottom() - getTop());
if (tappedX) {
// Allow clear events only when the transition is not in progress
if (!isTransitionInProgress.get()) {
if (!mDisableDefaultFunc) {
// Call the default functionality only if it wasn't disabled
mDefaultClearListener.onClear();
}
if (null != onClearListener) {
// Call the custom clear listener so that any member listening is notified of the clear event
onClearListener.onClear();
}
}
}
// Pass the touch event to other listeners, if none, the normal flow is resumed
return null != mOnTouchListener && mOnTouchListener.onTouch(v, event);
}
@Override
public void setOnTouchListener(OnTouchListener l) {
// Instead of using the super, we manually handle the touch event (only one listener can exist normally at a
// time)
mOnTouchListener = l;
}
private void init() {
// Set the bounds of the button
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
if (getCompoundDrawablePadding() == 0) {
// We want to have some default padding, in case no one is specified in the xml
setCompoundDrawablePadding(Dimensions.dpToPx(getContext(), 5f));
}
enableTransitionClearButton();
// if the clear button is pressed, fire up the handler. Otherwise do nothing
super.setOnTouchListener(this);
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
updateClearButton();
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
updateClearButton();
}
/**
* When hiding/showing the clear button an transition drawable will be used instead of the default drawable res.
*/
public void enableTransitionClearButton() {
mTransitionClearButtonShow =
(TransitionDrawable) ContextCompat.getDrawable(getContext(), R.drawable.ic_clear_fade_in);
mTransitionClearButtonHide =
(TransitionDrawable) ContextCompat.getDrawable(getContext(), R.drawable.ic_clear_fade_out);
mTransitionClearButtonShow.setCrossFadeEnabled(true);
mTransitionClearButtonHide.setCrossFadeEnabled(true);
}
/**
* When hiding/showing the clear button the default drawable res will be used instead of the transition drawable.
*/
@SuppressWarnings("unused")
public void disableTransitionClearButton() {
mTransitionClearButtonShow = null;
isTransitionInProgress.set(false);
transitionHandler.removeCallbacks(hideClearButtonRunnable);
transitionHandler.removeCallbacks(showClearButtonRunnable);
}
/**
* Set an custom listener which will get notified when an clear event was triggered from the clear button.
*
* @param clearListener
* The listener
* @param disableDefaultFunc
* {@code true} to disable the default clear functionality, usually meaning it will be
* handled by the
* calling member. {@code false} allow the default functionality of clearing the input.
*
* @see #setOnClearListener(OnClearListener)
*/
public void setOnClearListener(final OnClearListener clearListener, boolean disableDefaultFunc) {
this.onClearListener = clearListener;
this.mDisableDefaultFunc = disableDefaultFunc;
}
/**
* Set an custom listener which will get notified when an clear event was triggered from the clear button.
*
* @param clearListener
* The listener
*
* @see #setOnClearListener(OnClearListener, boolean)
*/
public void setOnClearListener(final OnClearListener clearListener) {
setOnClearListener(clearListener, false);
}
/**
* Disable the default functionality of the clear event - calling this won't allow for the input to be
* automatically
* cleared (it will give the ability to make custom implementations and react to the event before the clear).
*/
@SuppressWarnings("unused")
public void disableDefaultClearFunctionality() {
mDisableDefaultFunc = true;
}
/**
* Enable the default functionality of the clear event. Will automatically clear the input when the clear button is
* clicked.
*/
@SuppressWarnings("unused")
public void enableDefaultClearFunctionality() {
mDisableDefaultFunc = false;
}
/**
* Automatically show/hide the clear button based on the current input detected.
* <br/>
* If there is no input the clear button will be hidden. If there is input detected the clear button will be shown.
*/
public void updateClearButton() {
if (isEmpty()) {
hideClearButton();
} else {
showClearButton();
}
}
private boolean isEmpty() {
if (null == getText()) {
// Invalid editable text
return true;
} else if (TextUtils.isEmpty(getText().toString())) {
// Empty
return true;
} else if (TextUtils.isEmpty(getText().toString().trim())) {
// White spaces only
return true;
}
return false;
}
/**
* Hide the clear button.
* <br/>
* If an transition drawable was provided, it will be used to create an fade out effect, otherwise the default
* drawable resource will be used.
*/
public void hideClearButton() {
if (getCompoundDrawables()[2] == null) {
// The clear button was already hidden - do nothing
return;
}
if (null == mTransitionClearButtonHide) {
setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
return;
}
mTransitionClearButtonHide.resetTransition();
isTransitionInProgress.set(true);
mTransitionClearButtonHide.startTransition(TRANSITION_TIME);
setCompoundDrawablesWithIntrinsicBounds(null, null, mTransitionClearButtonHide, null);
transitionHandler.removeCallbacks(showClearButtonRunnable);
transitionHandler.removeCallbacks(hideClearButtonRunnable);
transitionHandler.postDelayed(hideClearButtonRunnable, TRANSITION_TIME);
}
/**
* Show the clear button.
* <br/>
* If an transition drawable was provided, it will be used to create an fade in effect, otherwise the default
* drawable resource will be used.
*/
public void showClearButton() {
if (getCompoundDrawables()[2] != null) {
// The clear button was already set - do nothing
return;
}
if (null == mTransitionClearButtonShow) {
setCompoundDrawablesWithIntrinsicBounds(0, 0, mImgClearButtonRes, 0);
return;
}
isTransitionInProgress.set(true);
mTransitionClearButtonShow.startTransition(TRANSITION_TIME);
setCompoundDrawablesWithIntrinsicBounds(null, null, mTransitionClearButtonShow, null);
transitionHandler.removeCallbacks(hideClearButtonRunnable);
transitionHandler.removeCallbacks(showClearButtonRunnable);
transitionHandler.postDelayed(showClearButtonRunnable, TRANSITION_TIME);
}
/**
* Custom contract which is used to notify any listeners that an clear event was triggered.
*/
public interface OnClearListener {
/**
* Clear event from the clear button triggered.
*/
void onClear();
}
}
当我想显示错误时,我使用以下命令:
// Enable and show the error
mClearView.requestFocus(); // we need to request focus, otherwise the error won't be shown
mClearView.setError("Please choose an valid destination!", null);
如果我使用:
mClearView.setError("Please choose an valid destination!")
,以便显示错误图标,之后不再显示清除按钮。
答案 0 :(得分:4)
由于setCompoundDrawablesWithIntrinsicBounds()
的文档(不是这样)明确指出后续调用应覆盖先前设置的drawable,而setError()
的文档说明如下:
将右侧复合可绘制的TextView 设置为“错误”图标,并设置将显示在“错误”图标中的错误消息 当TextView具有焦点时弹出。
我认为这是一个错误。
在设置新的drawable之前,调用setError(null)
并将先前设置的drawable设置为null
是一种可能的解决方法:
void setError(String error) {
editText.setError(error);
}
void setCompoundDrawableRight(Drawable rightDrawable) {
editText.setError(null);
editText.setCompoundDrawables(null, null, null, null);
editText.setCompoundDrawablesWithIntrinsicBounds(null, null, rightDrawable, null);
}