1

I have a custom edittext control which has a clear (x) icon set on the right when it's in focus and has text. Clicking the clear icon removes the text from the textbox. Unfortunately, when you click into the textbox, the focus change event is fired infinitely, as changing the compound drawable within the focus change listener seems to fire off two more focus change events, the first with the focus off, and the second with the focus back on. Any idea how I can get this working without the infinite loop?

Here is the code:

public class CustomEditText : EditText {
        private Drawable clearButton;

        protected CustomEditText (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {
        }

        public CustomEditText (Context context) : base (context) {
            Init ();
        }

        public CustomEditText (Context context, IAttributeSet attrs) : base (context, attrs) {
            Init (attrs);
        }

        public CustomEditText (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) {
            Init (attrs);
        }

        protected void Init (IAttributeSet attrs = null) {
            // Set up clear button
            SetupClearButton ();
            SetupEvents ();
        }

        private void SetupClearButton () {
            clearButton = ContextCompat.GetDrawable (Android.App.Application.Context, Resource.Drawable.forms_edit_text_clear_gray);
            clearButton.SetBounds (0, 0, clearButton.IntrinsicWidth, clearButton.IntrinsicHeight);
        }

        private void SetupEvents () {
            // Handle clear button visibility
            this.TextChanged += (sender, e) => {
                if (this.HasFocus)
                    UpdateClearButton ();
            };
            this.FocusChange += (object sender, FocusChangeEventArgs e) => {
                UpdateClearButton (e.HasFocus);// Gets called infinitely
            };

            // Handle clearing the text
            this.Touch += (sender, e) => {
                if (this.GetCompoundDrawables ()[2] != null && 
                    e.Event.Action == MotionEventActions.Up &&
                    e.Event.GetX () > (this.Width - this.PaddingRight - clearButton.IntrinsicWidth)) {
                    this.Text = "";
                    UpdateClearButton ();
                    e.Handled = true;
                } else
                    e.Handled = false;
            };
        }

        private void UpdateClearButton (bool hasFocus = true) {
            var compoundDrawables = this.GetCompoundDrawables ();
            var compoundDrawable = this.Text.Length == 0 || !hasFocus ? null : clearButton;
            if (compoundDrawables[2] != compoundDrawable)
                this.SetCompoundDrawables (compoundDrawables[0], compoundDrawables[1], compoundDrawable, compoundDrawables[3]);
        }
    }
Ryan M
  • 18,333
  • 31
  • 67
  • 74
Justin
  • 17,670
  • 38
  • 132
  • 201

2 Answers2

1

I ported DroidParts' ClearableEditText to Xamarin.Android to use when using the Android's Support Library widgets were not appropriate.

Note: DroidParts is under Apache 2.0 license so I can not post my C# derivative in full to StackOverflow, but the key to avoiding the continuous focus changing is in the OnTouch and OnFocusChange methods and the fact that the listeners are added to the base EditText Widget.

Full Code @ https://gist.github.com/sushihangover/01a7965aae75d8ef0589697aa8f0e750

public bool OnTouch(View v, MotionEvent e)
{
    if (GetDisplayedDrawable() != null)
    {
        int x = (int)e.GetX();
        int y = (int)e.GetY();
        int left = (loc == Location.LEFT) ? 0 : Width - PaddingRight - xD.IntrinsicWidth;
        int right = (loc == Location.LEFT) ? PaddingLeft + xD.IntrinsicWidth : Width;
        bool tappedX = x >= left && x <= right && y >= 0 && y <= (Bottom - Top);
        if (tappedX)
        {
            if (e.Action == MotionEventActions.Up)
            {
                Text = "";
                if (listener != null)
                {
                    listener.DidClearText();
                }
            }
            return true;
        }
    }
    if (l != null)
        return l.OnTouch(v, e);
    return false;
}

public void OnFocusChange(View v, bool hasFocus)
{
    if (hasFocus)
        SetClearIconVisible(!string.IsNullOrEmpty(Text));
    else
        SetClearIconVisible(false);
    if (f != null)
        f.OnFocusChange(v, hasFocus);
}

enter image description here

enter image description here

enter image description here

Original StackOverflow Q/A: How to create EditText with cross(x) button at end of it?

SushiHangover
  • 73,120
  • 10
  • 106
  • 165
1

In my opinion, the easiest implementation I have is this :

 public class ClearableEditext : EditText
{
    Context mContext;
    Drawable imgX;

    public ClearableEditext(Context context) : base(context)
    {
        init(context, null);
    }
    public ClearableEditext(Context context, Android.Util.IAttributeSet attrs) : base(context, attrs)
    {
        init(context, attrs);
    }
    public ClearableEditext(Context context, Android.Util.IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
    {
        init(context, attrs);
    }
    public ClearableEditext(Context context, Android.Util.IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
    {
        init(context, attrs);
    }

    public void init(Context ctx, Android.Util.IAttributeSet attrs)
    {
        mContext = ctx;
        imgX = ContextCompat.GetDrawable(ctx, Android.Resource.Drawable.PresenceOffline);
        imgX.SetBounds(0, 0, imgX.IntrinsicWidth, imgX.IntrinsicHeight);
        manageClearButton();
        this.SetOnTouchListener(new TouchHelper(this, imgX));
        this.AddTextChangedListener(new TextListener(this));
    }

    public void manageClearButton()
    {
        if (this.Text.ToString().Equals(""))
            removeClearButton();
        else
            addClearButton();
    }
    public void addClearButton()
    {
        this.SetCompoundDrawables(this.GetCompoundDrawables()[0],
                this.GetCompoundDrawables()[1],
                imgX,
                this.GetCompoundDrawables()[3]);
    }
    public void removeClearButton()
    {
        this.SetCompoundDrawables(this.GetCompoundDrawables()[0],
                this.GetCompoundDrawables()[1],
                null,
                this.GetCompoundDrawables()[3]);
    }
}

public class TouchHelper : Java.Lang.Object, View.IOnTouchListener
{
    ClearableEditext Editext;
    public ClearableEditext objClearable { get; set; }
    Drawable imgX;
    public TouchHelper(ClearableEditext editext, Drawable imgx)
    {
        Editext = editext;
        objClearable = objClearable;
        imgX = imgx;
    }
    public bool OnTouch(View v, MotionEvent e)
    {
        ClearableEditext et = Editext;

        if (et.GetCompoundDrawables()[2] == null)
            return false;
        // Only do this for up touches
        if (e.Action != MotionEventActions.Up)
            return false;
        // Is touch on our clear button?
        if (e.GetX() > et.Width - et.PaddingRight - imgX.IntrinsicWidth)
        {
            Editext.Text = string.Empty;
            if (objClearable != null)
                objClearable.removeClearButton();

        }
        return false;
    }
}

public class TextListener : Java.Lang.Object, ITextWatcher
{
    public ClearableEditext objClearable { get; set; }
    public TextListener(ClearableEditext objRef)
    {
        objClearable = objRef;
    }

    public void AfterTextChanged(IEditable s)
    {

    }

    public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
    {

    }

    public void OnTextChanged(ICharSequence s, int start, int before, int count)
    {
        if (objClearable != null)
            objClearable.manageClearButton();
    }
}

Probably Sushi has a better answer but i would suggest you to try this one out.

To change the x icon as your custom one change the image in init()

FreakyAli
  • 13,349
  • 3
  • 23
  • 63