Search code examples
androidxamarin.formsxamarin.androidxamarin.forms.entryxamarin.forms-styles

Clear Button issue in Custom Entry Renderer for Android


I am creating a custom renderer for an entry. I am mainly trying to add an icon to the left of the entry field. I have achieved this using SetCompoundDrawablesRelativeWithIntrinsicBounds() in Android and using LeftView in iOS.

The problem occurs when ClearButtonVisibility is set to "while editing". After typing something in the entry and the clear button is clicked, the left icon is cleared along with the text (This only occurs in Android. In iOS, only the text is cleared. I realized the built-in clear button image is in the compound drawables so I am setting the compound drawables with my icon as well as the built-in icon.

What is the best workaround to this? I would like it so that when the clear button is clicked, only the text is cleared and not the icon.

Note: The icon is cleared momentarily. When any other event is fired, the icon returns.

CustomEntry - before click

CustomEntry - after click

CustomEntry - after some event

Android.Renderer:

[assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
namespace Styles.Android.Renderers
{
    public class CustomEntryRenderer : EntryRenderer
    {
        public CustomEntryRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            // update control UI
            UpdateControl();

            base.OnElementPropertyChanged(sender, e);
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            // update control UI
            UpdateControl();

            base.OnElementChanged(e);
        }

        private void UpdateControl()
        {
            if (Element == null || Control == null)
                return;

            var entry = Element as CustomEntry;

            // set border/background
            var gradientBackground = new GradientDrawable();
            gradientBackground.SetShape(ShapeType.Rectangle);
            gradientBackground.SetColor(Element.BackgroundColor.ToAndroid());
            gradientBackground.SetCornerRadius(entry.CornerRadius);
            gradientBackground.SetStroke(entry.BorderWidth, entry.BorderColor.ToAndroid());
            Control.SetBackground(gradientBackground);

            // set appropriate icon
            int resID = Resources.GetIdentifier("icon_image", "drawable", this.Context.PackageName);
            var drawable = ContextCompat.GetDrawable(this.Context, resID);

            Control.SetCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, Control.GetCompoundDrawables()[2], null);
            Control.CompoundDrawablePadding = entry.IconPadding;
            paddingLeft = entry.IconPadding;               

            // set padding
            Control.SetPadding(Control.PaddingLeft, Control.PaddingTop, Control.PaddingRight, Control.PaddingBottom);
        }
    }
}

Solution

  • You could overwrite OnTouchListener to clean the text and SetCompoundDrawable again.

    change your CustomRenderer like below:

    [assembly: ExportRenderer(typeof(CustomEntry), typeof(CustomEntryRenderer))]
    namespace Styles.Android.Renderers
    {
        public class CustomEntryRenderer : EntryRenderer
        {
            static Drawable drawableLeft;
            static Drawable drawableRight;
            public CustomEntryRenderer(Context context) : base(context)
            {
            }
    
            protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                // update control UI
                UpdateControl();
    
                base.OnElementPropertyChanged(sender, e);
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
            {
                // update control UI
                UpdateControl();
    
                base.OnElementChanged(e);
            }
    
            private void UpdateControl()
            {
                if (Element == null || Control == null)
                    return;
    
                var entry = Element as CustomEntry;
    
                // set border/background
                var gradientBackground = new GradientDrawable();
                gradientBackground.SetShape(ShapeType.Rectangle);
                gradientBackground.SetColor(Element.BackgroundColor.ToAndroid());
                gradientBackground.SetCornerRadius(entry.CornerRadius);
                gradientBackground.SetStroke(entry.BorderWidth, entry.BorderColor.ToAndroid());
                Control.SetBackground(gradientBackground);
    
                // set appropriate icon
                int resID = Resources.GetIdentifier("icon_image", "drawable", this.Context.PackageName);
                drawableLeft = ContextCompat.GetDrawable(this.Context, resID);
                drawableRight = Control.GetCompoundDrawables()[2];
                Control.SetCompoundDrawablesRelativeWithIntrinsicBounds(drawableLeft, null, drawableRight , null);
                Control.CompoundDrawablePadding = entry.IconPadding;
                paddingLeft = entry.IconPadding;               
    
                // set padding
                Control.SetPadding(Control.PaddingLeft, Control.PaddingTop, Control.PaddingRight, Control.PaddingBottom);
    
                Control.SetOnTouchListener(new OnDrawableTouchListener());
            }
    
         public class OnDrawableTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
         {
            public bool OnTouch(Android.Views.View v, MotionEvent e)
            {
                if (v is EditText && e.Action == MotionEventActions.Up)
                {
                    EditText editText = (EditText)v;
                    editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(drawableLeft, null, drawableRight, null);
                    if (drawableRight != null)
                    {
    
                        bool touchable = e.GetX() > (editText.Width
                        - editText.PaddingRight - drawableRight.IntrinsicWidth)
                        && (e.GetX() < ((editText.Width - editText.PaddingRight)));
                        if (touchable)
                        {
                            Console.WriteLine(editText.Text);
                            editText.Text = "";
                            editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(drawableLeft, null, drawableRight, null);
                            return true;
                        }
    
                    }
                }
    
                return false;
            }
    
         }
    
        }
    }