131

In my app I have a EditText with a search Icon on the right side. I used the code given below.

 <EditText
        android:id="@+id/search"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_margin="4dip"
        android:layout_weight="1"
        android:background="@drawable/textfield_search1"
        android:drawableLeft="@drawable/logo"
        android:drawableRight="@drawable/search_icon"
        android:hint="Search Anything..."
        android:padding="4dip"
        android:singleLine="true" />

I want to set the onClickListener for the search icon image assigned to the right drawable of EditText. How is it possible?

Cœur
  • 34,719
  • 24
  • 185
  • 251
user965071
  • 1,589
  • 5
  • 15
  • 16
  • 1
    dupplicate http://stackoverflow.com/questions/3554377/handling-click-events-on-a-drawable-within-an-edittext – Tobrun Oct 30 '12 at 08:54
  • 1
    Simplest solution using Android provided API http://stackoverflow.com/a/26269435/185022 :-) – AZ_ Dec 16 '14 at 03:37
  • This is the best answer http://stackoverflow.com/a/37032927/346309 – JPM Sep 27 '16 at 18:38
  • If you are using `TextInputLayout` see https://stackoverflow.com/a/65940540/9723204. It's simple and elegant. – Hawklike May 10 '22 at 09:51

6 Answers6

375

Simple Solution, use methods that Android has already given, rather than reinventing wheeeeeeeeeel :-)

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;
        }
    });
AZ_
  • 36,879
  • 28
  • 155
  • 201
  • Error : java.lang.ArrayIndexOutOfBoundsException: length=4; index=2130837593 – user2273146 Nov 16 '14 at 06:47
  • 2
    @user2273146 if your length it 4 then your index must be 3. because length starts from 1, there is no 0 length but if length is 1 then index is 0 – AZ_ Nov 17 '14 at 01:32
  • 4
    Worked for me. Here is the code for pressing a left icon... if(event.getRawX() <= (editComment.getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width())) – kwh Jan 18 '15 at 16:38
  • 5
    getRawX() is not working for me ;So i used getX() and it worked! – dd619 Mar 04 '15 at 10:58
  • @dd619 Please tell me Android OS version, Mobile handset model, are you using stock rooted stock ROM !! getRawX( ) should work. Read this also http://stackoverflow.com/questions/20636163/difference-between-motionevent-getrawx-and-motionevent-getx – AZ_ Mar 05 '15 at 08:59
  • 8
    For me it worked for (event.getRawX()+editComment.getPaddingLeft()) <= (editComment.getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width()+editComment.getLeft()) where to edit text I have applied drawable paddng – Shoaib Chikate Jul 26 '15 at 17:46
  • @AZ_ I have another edittext below, and whenever my activity is getting started second edittext is getting the focus rather than the first one which is having DRAWABLE_RIGHT. What should I do to take focus of the first one? – Salman Khan Sep 11 '15 at 12:45
  • @SalmanKhan http://developer.android.com/reference/android/view/View.html#requestFocus() or use a handler with some delay to request focus if this doesn't works. – AZ_ Sep 15 '15 at 04:48
  • This solution works, but I cannot enter password on the field. – RoCk RoCk May 30 '16 at 03:51
  • soultion worked but when you change orientation its failed – Rohitashv jain Jun 23 '16 at 12:25
  • This worked perfectly for me.. didn't tried orientation thing.. – Rizwan Sohaib Aug 16 '16 at 14:40
  • @can you please edit your answer for the multi locale, as this didn't work . – Reprator Oct 05 '16 at 06:11
  • 9
    `ACTION_UP` is not working for me. I used `ACTION_DOWN` instead – cnbandicoot Jan 16 '17 at 11:15
  • This solution works also for TextView. Please, change `editComment.getRight() - editComment.getCompoundDrawables()` to `v.getRight() - ((EditText) v).getCompoundDrawables()`. If somebody handles `onClick` on text, change `MotionEvent.ACTION_UP` to `MotionEvent.ACTION_DOWN`. – CoolMind Apr 17 '17 at 14:12
  • Great.. You are awesome.. This must be the accepted answer.. – Monish Kamble Aug 05 '17 at 06:28
  • What a simple solution, amazing! – Botond Kopacz Aug 15 '17 at 18:17
  • 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. As well this is non-reusable. – Sotti Dec 17 '17 at 12:34
  • On the latest android studio version (A.S 3.0 up), if you call `setOnTouchListener` of a `View` class without subclassing the whole class you'll get this kind of warning (https://stackoverflow.com/q/47107105/3947487). The only solution that could fix that warning right now was to subclassing the whole class and call `view.performClick()` inside `onTouch()` method – nanangarsyad Dec 26 '17 at 09:05
  • How can i use it inside Horizontal scroll view ? – Shujat Munawar Apr 01 '18 at 16:21
  • If I use ACTION_UP it brings Clipboard dialog(CUT/PASTE) up on drawable click. I changed it to ACTION_DOWN and it looks fine now. Nice solution by the way. – IldiX Apr 23 '19 at 07:31
  • Seems to have an issue with more than 1 edittext inside a linearlayout with horizontal orientation. Click is always triggered. – ads Oct 01 '19 at 03:59
  • is there any hack to handle RTL here? – Abdulmalek Dery Oct 21 '19 at 13:21
  • 1
    @AbdulmalekDery probably what you need is reverse logic !!! – AZ_ Oct 22 '19 at 10:25
  • 1
    This worked like magic for me. – Jean Eric Oct 24 '19 at 14:31
  • U gotta love android API – Kebab Krabby May 25 '20 at 12:16
  • this will not work until you add this line to XML: android:longClickable="false" – enam Oct 21 '20 at 22:43
  • 1
    nullpointerexception if no drawables present, introduce an extra if for it – Atys Dec 01 '21 at 20:35
  • It works but it doesn't display soft input keyboard for typing if user do not click on drawable – Publius Flavius Tiberium Dec 05 '21 at 06:58
113
public class CustomEditText extends androidx.appcompat.widget.AppCompatEditText {

    private Drawable drawableRight;
    private Drawable drawableLeft;
    private Drawable drawableTop;
    private Drawable drawableBottom;

    int actionX, actionY;

    private DrawableClickListener clickListener;

    public CustomEditText (Context context, AttributeSet attrs) {
        super(context, attrs);
        // this Contructure required when you are using this view in xml
    }

    public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);        
    }

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    public void setCompoundDrawables(Drawable left, Drawable top,
            Drawable right, Drawable bottom) {
        if (left != null) {
            drawableLeft = left;
        }
        if (right != null) {
            drawableRight = right;
        }
        if (top != null) {
            drawableTop = top;
        }
        if (bottom != null) {
            drawableBottom = bottom;
        }
        super.setCompoundDrawables(left, top, right, bottom);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Rect bounds;
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            actionX = (int) event.getX();
            actionY = (int) event.getY();
            if (drawableBottom != null
                    && drawableBottom.getBounds().contains(actionX, actionY)) {
                clickListener.onClick(DrawablePosition.BOTTOM);
                return super.onTouchEvent(event);
            }

            if (drawableTop != null
                    && drawableTop.getBounds().contains(actionX, actionY)) {
                clickListener.onClick(DrawablePosition.TOP);
                return super.onTouchEvent(event);
            }

            // this works for left since container shares 0,0 origin with bounds
            if (drawableLeft != null) {
                bounds = null;
                bounds = drawableLeft.getBounds();

                int x, y;
                int extraTapArea = (int) (13 * getResources().getDisplayMetrics().density  + 0.5);

                x = actionX;
                y = actionY;

                if (!bounds.contains(actionX, actionY)) {
                    /** Gives the +20 area for tapping. */
                    x = (int) (actionX - extraTapArea);
                    y = (int) (actionY - extraTapArea);

                    if (x <= 0)
                        x = actionX;
                    if (y <= 0)
                        y = actionY;

                    /** Creates square from the smallest value */
                    if (x < y) {
                        y = x;
                    }
                }

                if (bounds.contains(x, y) && clickListener != null) {
                    clickListener
                            .onClick(DrawableClickListener.DrawablePosition.LEFT);
                    event.setAction(MotionEvent.ACTION_CANCEL);
                    return false;

                }
            }

            if (drawableRight != null) {

                bounds = null;
                bounds = drawableRight.getBounds();

                int x, y;
                int extraTapArea = 13;

                /**
                 * IF USER CLICKS JUST OUT SIDE THE RECTANGLE OF THE DRAWABLE
                 * THAN ADD X AND SUBTRACT THE Y WITH SOME VALUE SO THAT AFTER
                 * CALCULATING X AND Y CO-ORDINATE LIES INTO THE DRAWBABLE
                 * BOUND. - this process help to increase the tappable area of
                 * the rectangle.
                 */
                x = (int) (actionX + extraTapArea);
                y = (int) (actionY - extraTapArea);

                /**Since this is right drawable subtract the value of x from the width 
                * of view. so that width - tappedarea will result in x co-ordinate in drawable bound. 
                */
                x = getWidth() - x;
                
                 /*x can be negative if user taps at x co-ordinate just near the width.
                 * e.g views width = 300 and user taps 290. Then as per previous calculation
                 * 290 + 13 = 303. So subtract X from getWidth() will result in negative value.
                 * So to avoid this add the value previous added when x goes negative.
                 */
                 
                if(x <= 0){
                    x += extraTapArea;
                }
                
                 /* If result after calculating for extra tappable area is negative.
                 * assign the original value so that after subtracting
                 * extratapping area value doesn't go into negative value.
                 */               
                 
                if (y <= 0)
                    y = actionY;                

                /**If drawble bounds contains the x and y points then move ahead.*/
                if (bounds.contains(x, y) && clickListener != null) {
                    clickListener
                            .onClick(DrawableClickListener.DrawablePosition.RIGHT);
                    event.setAction(MotionEvent.ACTION_CANCEL);
                    return false;
                }
                return super.onTouchEvent(event);
            }           

        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void finalize() throws Throwable {
        drawableRight = null;
        drawableBottom = null;
        drawableLeft = null;
        drawableTop = null;
        super.finalize();
    }

    public void setDrawableClickListener(DrawableClickListener listener) {
        this.clickListener = listener;
    }

}

Also Create an Interface with

public interface DrawableClickListener {

    public static enum DrawablePosition { TOP, BOTTOM, LEFT, RIGHT };
    public void onClick(DrawablePosition target); 
    }

Still if u need any help, comment

Also set the drawableClickListener on the view in activity file.

editText.setDrawableClickListener(new DrawableClickListener() {
        
         
        public void onClick(DrawablePosition target) {
            switch (target) {
            case LEFT:
                //Do something here
                break;

            default:
                break;
            }
        }
        
    });
Simran Sharma
  • 645
  • 5
  • 13
Hardik4560
  • 3,137
  • 1
  • 17
  • 30
  • I would have also passed the Edittext as the parameter to the callback. But a good answer never the less. – st0le Oct 30 '12 at 09:02
  • I created the custom EditText. If i touch anywhere in the its going to the onTouchMovement method. but never goes to here "if (bounds.contains(x, y) && clickListener != null) { clickListener .onClick(DrawableClickListener.DrawablePosition.RIGHT); event.setAction(MotionEvent.ACTION_CANCEL); return false; }" – user965071 Oct 30 '12 at 10:47
  • Please see the edited answer. – Hardik4560 Oct 30 '12 at 11:07
  • 3
    Hi, I am not find CoreSystemContext.SCALE , so code shows Error. how I can get CoreSystemContext.SCALE. Please advise. – arefin May 21 '13 at 20:53
  • 3
    That is the variable holding device density. use getResources().getDisplayMetrics().density instead of that, you might need to create a static varaible in some other file. This to convert px to dp. A simple solution would be to remove that variable. – Hardik4560 May 22 '13 at 03:54
  • 1
    Please add the statement `playSoundEffect(android.view.SoundEffectConstants.CLICK);` after `if (event.getAction() == MotionEvent.ACTION_DOWN) {` in `onTouchEvent(MotionEvent event)` method so that user can get a click sound. – Sankar V Aug 21 '13 at 15:11
  • If you cant find CoreSystemContext.SCALE, just Change this line int extraTapArea = (int) (13 * getResources().getDisplayMetrics().density + 0.5); – Kokusho Jan 20 '15 at 17:29
  • Too much code. A simple setOnTouchListener will work with this. – V_J Oct 04 '16 at 12:29
  • @V-J can you comment a sample working code :D – Libin Thomas Feb 19 '18 at 07:10
39

This has been already answered but I tried a different way to make it simpler.

The idea is using putting an ImageButton on the right of EditText and having negative margin to it so that the EditText flows into the ImageButton making it look like the Button is in the EditText.

enter image description here

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <EditText
            android:id="@+id/editText"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:hint="Enter Pin"
            android:singleLine="true"
            android:textSize="25sp"
            android:paddingRight="60dp"
            />
        <ImageButton
            android:id="@+id/pastePin"
            android:layout_marginLeft="-60dp"
            style="?android:buttonBarButtonStyle"
            android:paddingBottom="5dp"
            android:src="@drawable/ic_action_paste"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

Also, as shown above, you can use a paddingRight of similar width in the EditText if you don't want the text in it to be flown over the ImageButton.

I guessed margin size with the help of android-studio's layout designer and it looks similar across all screen sizes. Or else you can calculate the width of the ImageButton and set the margin programatically.

MeetM
  • 1,380
  • 16
  • 25
  • 1
    It's the simple stuff that works best. – DariusL Mar 17 '16 at 09:08
  • 1
    this is redundant work for single drawable – Anand Savjani May 03 '16 at 05:41
  • margin and padding issues will be there for variety of devices – SMR Jun 20 '17 at 07:16
  • 2
    Linter will give you a warning "inefficient layout tree, use compound drawable", also more code than drawableRight=""... – 最白目 Jul 18 '17 at 12:31
  • 1
    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 Dec 17 '17 at 12:34
  • why not use simple FrameLayout ? – Xan Feb 27 '19 at 21:19
  • `having negative margin to it` That! There goes a hack into it. I would always implement a cleaner solution. The hack is bound to bite you some time later in future. – JaydeepW Oct 23 '19 at 08:22
6

You don't have access to the right image as far my knowledge, unless you override the onTouch event. I suggest to use a RelativeLayout, with one editText and one imageView, and set OnClickListener over the image view as below:

<RelativeLayout
        android:id="@+id/rlSearch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:drawable/edit_text"
        android:padding="5dip" >

        <EditText
            android:id="@+id/txtSearch"
            android:layout_width="match_parent"

            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/imgSearch"
            android:background="#00000000"
            android:ems="10"/>

        <ImageView
            android:id="@+id/imgSearch"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:src="@drawable/btnsearch" />
    </RelativeLayout>
Daryl Bennett
  • 462
  • 9
  • 21
jeet
  • 28,501
  • 6
  • 50
  • 52
  • 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 Dec 17 '17 at 12:33
0

I know this is quite old, but I recently had to do something very similar, and came up with a much simpler solution.

It boils down to the following steps:

  1. Create an XML layout that contains the EditText and Image
  2. Subclass FrameLayout and inflate the XML layout
  3. Add code for the click listener and any other behavior you want... without having to worry about positions of the click or any other messy code.

See this post for the full example: Handling click events on a drawable within an EditText

Community
  • 1
  • 1
Justin
  • 6,464
  • 6
  • 36
  • 34
-2

Please use below trick:

  • Create an image button with your icon and set its background color to be transparent.
  • Put the image button on the EditText
  • Implement the 'onclic'k listener of the button to execute your function
Reporter
  • 3,776
  • 5
  • 31
  • 46
Chirag
  • 56,384
  • 29
  • 154
  • 197
  • what if the size of the editText increase ?, what if I have many editText which requires this, which usually happens. – Hardik4560 Oct 30 '12 at 09:07
  • You need to understand the question! He/she isn't asking an alternative solution. – Nizzy Apr 30 '13 at 21:34
  • 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 Dec 17 '17 at 12:34