1

我有一個自定義小部件,它有一個清晰的按鈕設置爲使用setCompoundDrawablesWithIntrinsicBounds()方法的權限繪製。 現在我需要向小部件添加錯誤,並試圖使用默認功能,並依靠setError()方法。EditText setCompoundDrawablesWithIntrinsicBounds()不工作setError()

我遇到的問題是,在設置錯誤後,即使我之後再次設置,我的右側drawable也不會再顯示。

有沒有人遇到過這個問題,也許找到了解決問題的辦法?

謝謝。

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

發佈您的代碼,請。 – earthw0rmjim

回答

3

由於setCompoundDrawablesWithIntrinsicBounds()(沒有的話)的文件明確指出,後續的調用會覆蓋之前設置的圖形內容和setError()的文檔說以下內容:

設置右手化合物,可繪製將TextView添加到「錯誤」圖標,並設置當TextView具有焦點時將彈出的 中顯示的錯誤消息。

我認爲這是一個錯誤。

調用setError(null)和設置新的繪製之前設置預先設定的可繪製到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); 
} 
相關問題