Handling click events on a drawable within an EditText

157,009

Solution 1

Actually you don't need to extend any class. Let's say I have an EditText editComment with a drawableRight

editComment.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        final int DRAWABLE_LEFT = 0;
        final int DRAWABLE_TOP = 1;
        final int DRAWABLE_RIGHT = 2;
        final int DRAWABLE_BOTTOM = 3;

        if(event.getAction() == MotionEvent.ACTION_UP) {
            if(event.getRawX() >= (editComment.getRight() - editComment.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
                // your action here

                return true;
            }
        }
        return false;
    }
});

we getRawX() because we want to get the actual position of touch on screen, not relative to parent.

To get left side click

if(event.getRawX() <= (editComment.getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width())) 

Solution 2

Very, very good, thanks to everyone who contributed to this discussion. So if you don't want to deal with inconvenience of extending the class you can do the following (implemented for the right drawable only)

this.keyword = (AutoCompleteTextView) findViewById(R.id.search);
this.keyword.setOnTouchListener(new RightDrawableOnTouchListener(keyword) {
        @Override
        public boolean onDrawableTouch(final MotionEvent event) {
            return onClickSearch(keyword,event);
        }
    });

private boolean onClickSearch(final View view, MotionEvent event) {
    // do something
    event.setAction(MotionEvent.ACTION_CANCEL);
    return false;
}

And here's bare-bone listener implementation based on @Mark's answer

public abstract class RightDrawableOnTouchListener implements OnTouchListener {
    Drawable drawable;
    private int fuzz = 10;

    /**
     * @param keyword
     */
    public RightDrawableOnTouchListener(TextView view) {
        super();
        final Drawable[] drawables = view.getCompoundDrawables();
        if (drawables != null && drawables.length == 4)
            this.drawable = drawables[2];
    }

    /*
     * (non-Javadoc)
     * 
     * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
     */
    @Override
    public boolean onTouch(final View v, final MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN && drawable != null) {
            final int x = (int) event.getX();
            final int y = (int) event.getY();
            final Rect bounds = drawable.getBounds();
            if (x >= (v.getRight() - bounds.width() - fuzz) && x <= (v.getRight() - v.getPaddingRight() + fuzz)
                    && y >= (v.getPaddingTop() - fuzz) && y <= (v.getHeight() - v.getPaddingBottom()) + fuzz) {
                return onDrawableTouch(event);
            }
        }
        return false;
    }

    public abstract boolean onDrawableTouch(final MotionEvent event);

}

Solution 3

Consider the following. It's not the most elegant solution but it works, I just tested it.

  1. Create a customized EditText class CustomEditText.java:

    import android.content.Context;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.widget.EditText;
    
    public class CustomEditText extends EditText
    {
      private Drawable dRight;
      private Rect rBounds;
    
      public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
      }
      public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
      }
      public CustomEditText(Context context) {
        super(context);
      }
    
      @Override
      public void setCompoundDrawables(Drawable left, Drawable top,
          Drawable right, Drawable bottom)
      {
        if(right !=null)
        {
          dRight = right;
        }
        super.setCompoundDrawables(left, top, right, bottom);
      }
    
      @Override
      public boolean onTouchEvent(MotionEvent event)
      {
    
        if(event.getAction() == MotionEvent.ACTION_UP && dRight!=null)
        {
          rBounds = dRight.getBounds();
          final int x = (int)event.getX();
          final int y = (int)event.getY();
          //System.out.println("x:/y: "+x+"/"+y);
          //System.out.println("bounds: "+bounds.left+"/"+bounds.right+"/"+bounds.top+"/"+bounds.bottom);
          //check to make sure the touch event was within the bounds of the drawable
          if(x>=(this.getRight()-rBounds.width()) && x<=(this.getRight()-this.getPaddingRight())
              && y>=this.getPaddingTop() && y<=(this.getHeight()-this.getPaddingBottom()))
          {
            //System.out.println("touch");
            this.setText("");
            event.setAction(MotionEvent.ACTION_CANCEL);//use this to prevent the keyboard from coming up
          }
        }
        return super.onTouchEvent(event);
      }
    
      @Override
      protected void finalize() throws Throwable
      {
        dRight = null;
        rBounds = null;
        super.finalize();
      }
    }
    
  2. Change your layout XML to this (where com.example is your actual project package name):

    <com.example.CustomEditText
        android:id="@+id/txtsearch"
        …
        android:layout_gravity="center_vertical"
        android:background="@layout/shape"
        android:hint="Enter place,city,state"
        android:drawableRight="@drawable/cross" 
    />
    
  3. Finally, add this (or something similar) to your activity:

    …
    CustomEditText et = (CustomEditText) this.findViewById(R.id.txtsearch);
    …
    

I might be a bit off with the calculation of the touch bounds for the nested drawable but you get the idea.

I hope this helps.

Solution 4

I created a useful abstract class DrawableClickListener which implements OnTouchListener.

In addition to the DrawableClickListener class, I also created 4 additional abstract classes which extend the DrawableClickListener class and handle the clicking of the drawable area for the correct quadrant.

  • LeftDrawableClickListener
  • TopDrawableClickListener
  • RightDrawableClickListener
  • BottomDrawableClickListener

Point to Consider

One thing to consider is that the images are not resized if done this way; thus the images must be scaled correctly before being put into the res/drawable folder(s).

If you define a LinearLayout containing an ImageView and a TextView, it's a lot easier to manipulate the size of the image being displayed.


activity_my.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/myTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="replace this with a variable"
        android:textSize="30sp"
        android:drawableLeft="@drawable/my_left_image"
        android:drawableRight="@drawable/my_right_image"
        android:drawablePadding="9dp" />

</RelativeLayout>

MyActivity.java

package com.company.project.core;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MyActivity extends Activity
{

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_my );

        final TextView myTextView = (TextView) this.findViewById( R.id.myTextView );
        myTextView.setOnTouchListener( new DrawableClickListener.LeftDrawableClickListener(myTextView)
        {
            @Override
            public boolean onDrawableClick()
            {
                // TODO : insert code to perform on clicking of the LEFT drawable image...

                return true;
            }
        } );
        myTextView.setOnTouchListener( new DrawableClickListener.RightDrawableClickListener(myTextView)
        {
            @Override
            public boolean onDrawableClick()
            {
                // TODO : insert code to perform on clicking of the RIGHT drawable image...

                return true;
            }
        } );
    }

}

DrawableClickListener.java

package com.company.project.core;

import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;

/**
 * This class can be used to define a listener for a compound drawable.
 * 
 * @author Matthew Weiler
 * */
public abstract class DrawableClickListener implements OnTouchListener
{

    /* PUBLIC CONSTANTS */
    /**
     * This represents the left drawable.
     * */
    public static final int DRAWABLE_INDEX_LEFT = 0;
    /**
     * This represents the top drawable.
     * */
    public static final int DRAWABLE_INDEX_TOP = 1;
    /**
     * This represents the right drawable.
     * */
    public static final int DRAWABLE_INDEX_RIGHT = 2;
    /**
     * This represents the bottom drawable.
     * */
    public static final int DRAWABLE_INDEX_BOTTOM = 3;
    /**
     * This stores the default value to be used for the
     * {@link DrawableClickListener#fuzz}.
     * */
    public static final int DEFAULT_FUZZ = 10;

    /* PRIVATE VARIABLES */
    /**
     * This stores the number of pixels of &quot;fuzz&quot; that should be
     * included to account for the size of a finger.
     * */
    private final int fuzz;
    /**
     * This will store a reference to the {@link Drawable}.
     * */
    private Drawable drawable = null;

    /* CONSTRUCTORS */
    /**
     * This will create a new instance of a {@link DrawableClickListener}
     * object.
     * 
     * @param view
     *            The {@link TextView} that this {@link DrawableClickListener}
     *            is associated with.
     * @param drawableIndex
     *            The index of the drawable that this
     *            {@link DrawableClickListener} pertains to.
     *            <br />
     *            <i>use one of the values:
     *            <b>DrawableOnTouchListener.DRAWABLE_INDEX_*</b></i>
     */
    public DrawableClickListener( final TextView view, final int drawableIndex )
    {
        this( view, drawableIndex, DrawableClickListener.DEFAULT_FUZZ );
    }

    /**
     * This will create a new instance of a {@link DrawableClickListener}
     * object.
     * 
     * @param view
     *            The {@link TextView} that this {@link DrawableClickListener}
     *            is associated with.
     * @param drawableIndex
     *            The index of the drawable that this
     *            {@link DrawableClickListener} pertains to.
     *            <br />
     *            <i>use one of the values:
     *            <b>DrawableOnTouchListener.DRAWABLE_INDEX_*</b></i>
     * @param fuzzOverride
     *            The number of pixels of &quot;fuzz&quot; that should be
     *            included to account for the size of a finger.
     */
    public DrawableClickListener( final TextView view, final int drawableIndex, final int fuzz )
    {
        super();
        this.fuzz = fuzz;
        final Drawable[] drawables = view.getCompoundDrawables();
        if ( drawables != null && drawables.length == 4 )
        {
            this.drawable = drawables[drawableIndex];
        }
    }

    /* OVERRIDDEN PUBLIC METHODS */
    @Override
    public boolean onTouch( final View v, final MotionEvent event )
    {
        if ( event.getAction() == MotionEvent.ACTION_DOWN && drawable != null )
        {
            final int x = (int) event.getX();
            final int y = (int) event.getY();
            final Rect bounds = drawable.getBounds();
            if ( this.isClickOnDrawable( x, y, v, bounds, this.fuzz ) )
            {
                return this.onDrawableClick();
            }
        }
        return false;
    }

    /* PUBLIC METHODS */
    /**
     * 
     * */
    public abstract boolean isClickOnDrawable( final int x, final int y, final View view, final Rect drawableBounds, final int fuzz );

    /**
     * This method will be fired when the drawable is touched/clicked.
     * 
     * @return
     *         <code>true</code> if the listener has consumed the event;
     *         <code>false</code> otherwise.
     * */
    public abstract boolean onDrawableClick();

    /* PUBLIC CLASSES */
    /**
     * This class can be used to define a listener for a <b>LEFT</b> compound
     * drawable.
     * */
    public static abstract class LeftDrawableClickListener extends DrawableClickListener
    {

        /* CONSTRUCTORS */
        /**
         * This will create a new instance of a
         * {@link LeftDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link LeftDrawableClickListener} is associated with.
         */
        public LeftDrawableClickListener( final TextView view )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_LEFT );
        }

        /**
         * This will create a new instance of a
         * {@link LeftDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link LeftDrawableClickListener} is associated with.
         * @param fuzzOverride
         *            The number of pixels of &quot;fuzz&quot; that should be
         *            included to account for the size of a finger.
         */
        public LeftDrawableClickListener( final TextView view, final int fuzz )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_LEFT, fuzz );
        }

        /* PUBLIC METHODS */
        public boolean isClickOnDrawable( final int x, final int y, final View view, final Rect drawableBounds, final int fuzz )
        {
            if ( x >= ( view.getPaddingLeft() - fuzz ) )
            {
                if ( x <= ( view.getPaddingLeft() + drawableBounds.width() + fuzz ) )
                {
                    if ( y >= ( view.getPaddingTop() - fuzz ) )
                    {
                        if ( y <= ( view.getHeight() - view.getPaddingBottom() + fuzz ) )
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    }

    /**
     * This class can be used to define a listener for a <b>TOP</b> compound
     * drawable.
     * */
    public static abstract class TopDrawableClickListener extends DrawableClickListener
    {

        /* CONSTRUCTORS */
        /**
         * This will create a new instance of a {@link TopDrawableClickListener}
         * object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link TopDrawableClickListener} is associated with.
         */
        public TopDrawableClickListener( final TextView view )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_TOP );
        }

        /**
         * This will create a new instance of a {@link TopDrawableClickListener}
         * object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link TopDrawableClickListener} is associated with.
         * @param fuzzOverride
         *            The number of pixels of &quot;fuzz&quot; that should be
         *            included to account for the size of a finger.
         */
        public TopDrawableClickListener( final TextView view, final int fuzz )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_TOP, fuzz );
        }

        /* PUBLIC METHODS */
        public boolean isClickOnDrawable( final int x, final int y, final View view, final Rect drawableBounds, final int fuzz )
        {
            if ( x >= ( view.getPaddingLeft() - fuzz ) )
            {
                if ( x <= ( view.getWidth() - view.getPaddingRight() + fuzz ) )
                {
                    if ( y >= ( view.getPaddingTop() - fuzz ) )
                    {
                        if ( y <= ( view.getPaddingTop() + drawableBounds.height() + fuzz ) )
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    }

    /**
     * This class can be used to define a listener for a <b>RIGHT</b> compound
     * drawable.
     * */
    public static abstract class RightDrawableClickListener extends DrawableClickListener
    {

        /* CONSTRUCTORS */
        /**
         * This will create a new instance of a
         * {@link RightDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link RightDrawableClickListener} is associated with.
         */
        public RightDrawableClickListener( final TextView view )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_RIGHT );
        }

        /**
         * This will create a new instance of a
         * {@link RightDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link RightDrawableClickListener} is associated with.
         * @param fuzzOverride
         *            The number of pixels of &quot;fuzz&quot; that should be
         *            included to account for the size of a finger.
         */
        public RightDrawableClickListener( final TextView view, final int fuzz )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_RIGHT, fuzz );
        }

        /* PUBLIC METHODS */
        public boolean isClickOnDrawable( final int x, final int y, final View view, final Rect drawableBounds, final int fuzz )
        {
            if ( x >= ( view.getWidth() - view.getPaddingRight() - drawableBounds.width() - fuzz ) )
            {
                if ( x <= ( view.getWidth() - view.getPaddingRight() + fuzz ) )
                {
                    if ( y >= ( view.getPaddingTop() - fuzz ) )
                    {
                        if ( y <= ( view.getHeight() - view.getPaddingBottom() + fuzz ) )
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    }

    /**
     * This class can be used to define a listener for a <b>BOTTOM</b> compound
     * drawable.
     * */
    public static abstract class BottomDrawableClickListener extends DrawableClickListener
    {

        /* CONSTRUCTORS */
        /**
         * This will create a new instance of a
         * {@link BottomDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link BottomDrawableClickListener} is associated with.
         */
        public BottomDrawableClickListener( final TextView view )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_BOTTOM );
        }

        /**
         * This will create a new instance of a
         * {@link BottomDrawableClickListener} object.
         * 
         * @param view
         *            The {@link TextView} that this
         *            {@link BottomDrawableClickListener} is associated with.
         * @param fuzzOverride
         *            The number of pixels of &quot;fuzz&quot; that should be
         *            included to account for the size of a finger.
         */
        public BottomDrawableClickListener( final TextView view, final int fuzz )
        {
            super( view, DrawableClickListener.DRAWABLE_INDEX_BOTTOM, fuzz );
        }

        /* PUBLIC METHODS */
        public boolean isClickOnDrawable( final int x, final int y, final View view, final Rect drawableBounds, final int fuzz )
        {
            if ( x >= ( view.getPaddingLeft() - fuzz ) )
            {
                if ( x <= ( view.getWidth() - view.getPaddingRight() + fuzz ) )
                {
                    if ( y >= ( view.getHeight() - view.getPaddingBottom() - drawableBounds.height() - fuzz ) )
                    {
                        if ( y <= ( view.getHeight() - view.getPaddingBottom() + fuzz ) )
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

    }

}

Solution 5

Its very simple. Lets say you have a drawable on left side of your EditText 'txtsearch'. Following will do the trick.

EditText txtsearch = (EditText) findViewById(R.id.txtsearch);
txtsearch.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_UP) {
            if(event.getRawX() <= txtsearch.getTotalPaddingLeft()) {
                // your action for drawable click event

             return true;
            }
        }
        return false;
    }
});

If you want for right drawable change the if statement to:

if(event.getRawX() >= txtsearch.getRight() - txtsearch.getTotalPaddingRight())

Similarly, you can do it for all compound drawables.

txtsearch.getTotalPaddingTop()
txtsearch.getTotalPaddingBottom()

This method call returns all the padding on that side including any drawables. You can use this even for TextView, Button etc.

Click here for reference from android developer site.

Share:
157,009

Related videos on Youtube

Manikandan
Author by

Manikandan

Updated on April 08, 2022

Comments

  • Manikandan
    Manikandan about 2 years

    I have added an image right of the text in an EditText widget, using the following XML:

    <EditText
      android:id="@+id/txtsearch"
      ...
      android:layout_gravity="center_vertical"
      android:background="@layout/shape"
      android:hint="Enter place,city,state"
      android:drawableRight="@drawable/cross" />
    

    But I want to clear the EditText when the embedded image is clicked. How can I do this?

  • Thiago
    Thiago over 12 years
    It seems your code is not working, I have just tested and nothing happens when I touch the drawable.
  • Bob Barbara
    Bob Barbara almost 12 years
    Actually, I have heard that modifying the MotionEvent is discouraged practice, leading to undefined behaviour that would likely break on different platforms, so perhaps a better solution could be stackoverflow.com/a/6235602
  • Maksim Dmitriev
    Maksim Dmitriev over 11 years
    @RyanM, I used TextView instead of EditText. I took the code and if I click on the TextView (not the icon but on any space on the TextView), the method onTouchEvent(MotionEvent event) is called. So, I can implement OnClickListener for a usual TextView without any additional classes such as CustomEditText
  • Vino
    Vino over 11 years
    @RyanM, Instead of using this.getRight()-rBounds.width() why not use this.getMeasuredWidth() - this.getCompoundPaddingRight() ? Wouldn't it take care of drawable's padding and also get rid of the drawable's bound?
  • André
    André about 11 years
    You should add v.getLeft() to x and v.getTop() to y in order to get the correct position.
  • Speedy
    Speedy almost 11 years
    Actually you should replace v.getRight() by v.getWidth().
  • user1940676
    user1940676 over 10 years
    What about drawableLeft??
  • Admin
    Admin over 10 years
    Where did your onClickSearch method receive it's event?
  • Qadir Hussain
    Qadir Hussain over 10 years
    @RyanM how to change the image on touch of cross button click event?
  • Qadir Hussain
    Qadir Hussain over 10 years
    @user2848783 how to set this in my left drawable?
  • Pratik Butani
    Pratik Butani over 10 years
    Working after changed event.getRawX() instead of event.getX() @AngeloS
  • tomurka
    tomurka almost 10 years
    One note: replace "return false;" to "return true;" otherwise after ACTION_DOWN -> ACTION_UP will not be fired.
  • RaB
    RaB over 9 years
    note that your fuzz factor should scale with the DPI, 10px in ldpi is something completely different from 10px in xxhdpi.
  • Paul Verest
    Paul Verest over 9 years
    a bit improved answer at stackoverflow.com/questions/23184120/…
  • gonzalomelov
    gonzalomelov about 9 years
    You have add "- v.getPaddingLeft()" to the first part of the if "x >= (v.getRight() - bounds.width() - fuzz)".
  • kassim
    kassim about 9 years
    If you add padding you need to count that in as well as the getRight() gets the right of the TextView, which won't be the right of the drawable if there's padding. Adding - editComment.getPaddingRight() to the end of your if statement should work.
  • Mansukh Ahir
    Mansukh Ahir almost 9 years
    public final float getRawX ()Returns the original raw X coordinate of this event. For touch events on the screen, this is the original location of the event on the screen, before it had been adjusted for the containing window and views.
  • Chad Bingham
    Chad Bingham over 8 years
    You should check for negative. .width() does not check if it is returning a positive number. I just wrapped it with width = Math.abs(width);
  • Юрій Мазуревич
    Юрій Мазуревич over 8 years
    What is fuzz for? Please clarify.
  • Bianca Daniciuc
    Bianca Daniciuc over 8 years
    I think this is a fine answer, except the part where true is returned everywhere. I'd suggest to return true only when the event needs to be consumed (the touch gesture happened in the right area).
  • C0D3LIC1OU5
    C0D3LIC1OU5 over 8 years
    Used RelativeLayout to achieve proper positioning, just seems less convoluted than other solutions, and far less code to maintain.
  • Fletcher Johns
    Fletcher Johns over 8 years
    This does not work if the EditText's parent is not aligned with the left of the screen. You should use event.getX() instead of event.getRawX() and use editText.getWidth() instead of editText.getRight()
  • Tomask
    Tomask about 8 years
    Custom versions of EditText do not support proper widgets tinting when using appcompat on pre-lollipop devices. Use AppCompatEditText as a parent class of your custom EditText
  • Thunder Dragon
    Thunder Dragon over 7 years
    In my case it is not working! Here getBounds() returns Rect(0, 0 - -1, -1) . Does anyone have any clue why this is happening?
  • ban-geoengineering
    ban-geoengineering over 7 years
    It looks like fuzz effectively makes the tappable area a bit larger, making it easier to tap the small drawable.
  • Glenn
    Glenn over 6 years
    This worked for me but I had to use getX() instead of getRawX(). I think getRawX() only works if the view is on the left edge of the screen.
  • Sotti
    Sotti over 6 years
    The calculation of the positions is wrong. Is mixing absolute coordinates "getRawX()", with relative ones like "getRight()"
  • Sotti
    Sotti over 6 years
    The problem with this approach is that falls apart as soon as you start changing the text size on the EditText. You might think that's just on the developer's side but it's not as far as the devices have text size in settings.You can avoid this using dp instead of sp on the EditText but it just makes things worse. The other problems are things like handling multiline EditTexts.
  • Sotti
    Sotti over 6 years
    This is mixing absolute positions "getRawX" with relative positions "getRight". If you set a right or left margin on the editText you'll see how this breaks as the click is triggered on wrong coordinates.
  • Sotti
    Sotti over 6 years
    The problem with this approach is that falls apart as soon as you start changing the text size on the EditText. You might think that's just on the developer's side but it's not as far as the devices have text size in settings.You can avoid this using dp instead of sp on the EditText but it just makes things worse. The other problems are things like handling multiline EditTexts
  • Sotti
    Sotti over 6 years
    It's a little bit confusing that s TouchListener is handling the drawable visibility and the clear action itself. That's not a touch listener responsibility and the name of the class is misleading. As well as you are calculating relative positions is not necessary to remove margins from the equation. getRight - width will do it.
  • Sotti
    Sotti over 6 years
    The problem with this approach is that falls apart as soon as you start changing the text size on the EditText. You might think that's just on the developer's side but it's not as far as the devices have text size in settings.You can avoid this using dp instead of sp on the EditText but it just makes things worse. The other problems are things like handling multiline EditTexts.
  • Sotti
    Sotti over 6 years
    This is mixing absolute positions "getRawX" with relative positions "getRight". If you set a right or left margin on the editText you'll see how this breaks as the click is triggered on wrong coordinates.
  • Sotti
    Sotti over 6 years
    This is mixing absolute positions "getRawX" with relative positions "getRight". If you set a right or left margin on the editText you'll see how this breaks as the click is triggered on wrong coordinates.
  • Sotti
    Sotti over 6 years
    This is mixing absolute positions "getRawX" with relative positions "getRight". If you set a right or left margin on the editText you'll see how this breaks as the click is triggered on wrong coordinates.
  • Sotti
    Sotti over 6 years
    This is mixing absolute positions "getRawX" with relative positions "getRight". If you set a right or left margin on the editText you'll see how this breaks as the click is triggered on wrong coordinates.
  • Sotti
    Sotti over 6 years
    The problem with this approach is that falls apart as soon as you start changing the text size on the EditText. You might think that's just on the developer's side but it's not as far as the devices have text size in settings.You can avoid this using dp instead of sp on the EditText but it just makes things worse. The other problems are things like handling multiline EditTexts
  • Jevgenij Kononov
    Jevgenij Kononov over 6 years
    I have never used that for multi-line search, so sorry I never thought that this problem may appear. Probably blocking for multi line will help. Can you attach screenshot of app or view to see what happens? And I will try to resolved that and maybe help you (fix this code) and me for future use. Thanks.
  • Sotti
    Sotti over 6 years
    It's very easy to replicate, it even happens on the layout preview as soon as you add 2 lines.
  • zohaib khaliq
    zohaib khaliq over 6 years
    i have added right margin on edit text, my code still works perfect
  • Vickie Kangare
    Vickie Kangare over 6 years
    Make return true otherwise action will not perform.
  • Max Makeichik
    Max Makeichik almost 6 years
    I created an extension function for this case: ` @SuppressLint("ClickableViewAccessibility") inline fun TextView.onRightCompoundDrawableClick(crossinline f: () -> Unit) { this.setOnTouchListener(View.OnTouchListener { _, event -> val DRAWABLE_RIGHT = 2 if (event.action == MotionEvent.ACTION_UP) { if (event.rawX >= this.right - this.paddingRight - this.compoundDrawables[DRAWABLE_RIGHT].bounds.width()) { f() return@OnTouchListener true } } false }) } `
  • CoolMind
    CoolMind almost 6 years
    A background for EditText should be android:background="@android:color/transparent".
  • M. Reza Nasirloo
    M. Reza Nasirloo over 5 years
    What about the relative drawable position, Start, and End?
  • Sevastyan Savanyuk
    Sevastyan Savanyuk over 5 years
    View.getRight() returns relative value. You should add position of the View to that to get it right. You get the absolute position of the view by getLocationOnScreen(intArray) and then getting the x: intArray.get(0).
  • methodsignature
    methodsignature over 5 years
    "if you don't want to deal with the inconvenience of extending the class". Really wish we as programmer would work to extend our frameworks to our specific projects. If you need a component that acts like an EditText but supports compound drawable clicks, create one. Why expose how it works by externalizing the touch listener. Setting setOnTouchListener is not as clear as setOnRightIconClickListener.
  • farid_z
    farid_z over 4 years
    Needs to subtract right padding, if any, for edit text when determining the start of the hit rectangle.
  • P Kuijpers
    P Kuijpers over 4 years
    As mentioned before, the location of the view is not considered (rawX instead of X), so it'll only work when the EditText is clipped to the parent. The answer/link from Paul Verest improves this, but doesn't take clickable padding into account. If you like to include this, incl. some explanation how it works, then check out my answer here: stackoverflow.com/a/58163977/2660216
  • Shyam Sunder
    Shyam Sunder over 3 years
    Kotlin Extension's can be used here to make it look good & easy to use across the App. fun EditText.setRightIconClick(clickEvent: () -> Unit){ setOnTouchListener(object : View.OnTouchListener{ override fun onTouch(v: View?, event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_UP) { if (event.rawX >= right - compoundDrawables[2].bounds.width() ) { clickEvent.invoke() return true } } return false } }) }
  • Khizar Hayat
    Khizar Hayat almost 3 years
    Hats off Extension functions. thanks @mattia