Obsługa zdarzeń kliknięcia na wyciągu w obrębie EditText


239

Dodałem obraz z prawej strony tekstu w EditTextwidżecie, używając następującego kodu 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" />

Chcę jednak wyczyścić, EditTextkiedy kliknięty zostanie osadzony obraz. W jaki sposób mogę to zrobić?


Odpowiedzi:


358

W rzeczywistości nie musisz przedłużać żadnej klasy. Powiedzmy, że mam EditText editComment z 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;
    }
});

my, getRawX()ponieważ chcemy uzyskać rzeczywistą pozycję dotyku na ekranie, a nie względem rodzica.

Aby uzyskać lewy bok, kliknij

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

2
@ user2848783 jak ustawić to w mojej lewej do rysowania?
Qadir Hussain

10
Praca po zmianie event.getRawX()zamiast event.getX()@AngeloS
Pratik Butani

4
Jedna uwaga: zamień „return false;” „zwrócić true;” w przeciwnym razie po ACTION_DOWN -> ACTION_UP nie zostanie uruchomiony.
tomurka

9
Jeśli dodasz dopełnienie, musisz to policzyć, a także getRight()dostaniesz prawo do TextView, który nie będzie miał prawa do rysowania, jeśli będzie dopełnienie. Dodawanie - editComment.getPaddingRight()na końcu Twojego ifwyciągu powinno działać.
kassim

21
To nie działa, jeśli element nadrzędny EditText nie jest wyrównany do lewej strony ekranu. Powinieneś użyć event.getX () zamiast event.getRawX () i editText.getWidth () zamiast editText.getRight ()
Fletcher Johns

85

Bardzo, bardzo dobrze, dziękuję wszystkim, którzy przyczynili się do tej dyskusji. Jeśli więc nie chcesz uporać się z niedogodnościami związanymi z rozszerzeniem klasy, możesz wykonać następujące czynności (zaimplementowane tylko dla prawej strony do rysowania)

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;
}

A oto implementacja nasłuchującego gołej kości oparta na odpowiedzi @ Mark

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);

}

3
Powinieneś dodać v.getLeft () do x i v.getTop () do y, aby uzyskać prawidłową pozycję.
André

3
Właściwie należy zastąpić v.getRight()przez v.getWidth().
Speedy

2
zwróć uwagę, że współczynnik fuzz powinien być skalowany wraz z DPI, 10px w ldpi to coś zupełnie innego niż 10px w xxhdpi.
RaB

4
Do czego służy Fuzz? Proszę o wyjaśnienie.
Юрій Мазуревич

1
Wygląda na to, że fuzzskutecznie sprawia, że ​​obszar styku jest nieco większy, co ułatwia stuknięcie w mały uchwyt.
zakaz geoinżynierii

28

Rozważ następujące. To nie jest najbardziej eleganckie rozwiązanie, ale działa, właśnie go przetestowałem.

  1. Utwórz niestandardową EditTextklasę 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. Zmień XML układu na ten (gdzie com.examplejest rzeczywista nazwa pakietu projektu):

    <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. Na koniec dodaj to (lub coś podobnego) do swojej aktywności:

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

Mógłbym trochę obliczyć granice dotyku dla zagnieżdżonego rysowalnego, ale masz pomysł.

Mam nadzieję, że to pomoże.


Właściwie słyszałem, że modyfikowanie MotionEvent jest odradzaną praktyką, prowadzącą do nieokreślonego zachowania, które prawdopodobnie zepsuje się na różnych platformach, więc być może lepszym rozwiązaniem może być stackoverflow.com/a/6235602
Giulio Piancastelli

@RyanM, użyłem TextViewzamiast EditText. Wziąłem kod i jeśli kliknę TextView(nie ikonę, ale dowolne miejsce na TextView), metoda onTouchEvent(MotionEvent event)zostanie wywołana. Więc mogę wdrożyć OnClickListenerna zwykłe TextViewbez żadnych dodatkowych klas, takich jakCustomEditText
Maksim Dmitriev

@RyanM, Zamiast używać, this.getRight()-rBounds.width()dlaczego nie używać this.getMeasuredWidth() - this.getCompoundPaddingRight()? Czy nie zajęłoby się wypełnieniem szuflady, a także pozbył się jej granic?
Vino

@RyanM jak zmienić obraz po dotknięciu zdarzenia kliknięcia przycisku krzyżowego?
Qadir Hussain,

Niestandardowe wersje EditTextnie obsługują prawidłowego przyciemniania widżetów podczas używania appcompat na urządzeniach sprzed wersji Lollipop. Użyj AppCompatEditTextjako klasy nadrzędnej swojego niestandardowego
EditText

24

Stworzyłem użyteczną klasę abstrakcyjną DrawableClickListener, która implementuje OnTouchListener .

Oprócz klasy DrawableClickListener stworzyłem również 4 dodatkowe klasy abstrakcyjne, które rozszerzają klasę DrawableClickListener i obsługują klikanie obszaru do rysowania dla prawidłowego kwadrantu.

  • LeftDrawableClickListener
  • TopDrawableClickListener
  • RightDrawableClickListener
  • BottomDrawableClickListener

Wskaż do rozważenia

Jedną rzeczą do rozważenia jest to, że rozmiary obrazów nie są zmieniane, jeśli są wykonywane w ten sposób; dlatego obrazy muszą być poprawnie skalowane przed umieszczeniem w folderze (folderach) res / drawable .

Jeśli zdefiniujesz LinearLayout zawierający ImageView i TextView , o wiele łatwiej jest manipulować rozmiarem wyświetlanego obrazu.


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;
        }

    }

}

14

To jest bardzo proste. Powiedzmy, że po lewej stronie tekstu „txtsearch” możesz rysować. Następujący załatwi sprawę.

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;
    }
});

Jeśli chcesz uzyskać prawo do rysowania, zmień instrukcję if na:

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

Podobnie możesz to zrobić dla wszystkich złożonych rysunków.

txtsearch.getTotalPaddingTop()
txtsearch.getTotalPaddingBottom()

To wywołanie metody zwraca całe dopełnienie po tej stronie, w tym wszystkie drawable. Możesz użyć tego nawet do TextView, przycisku itp.

Kliknij tutaj, aby uzyskać informacje na stronie dewelopera Androida.


1
Myślę, że to dobra odpowiedź, z wyjątkiem tej części, w której prawda powraca wszędzie. Sugeruję zwrócenie wartości true tylko wtedy, gdy zdarzenie musi zostać zużyte (gest dotykowy miał miejsce w odpowiednim miejscu).
Bianca Daniciuc

12

Myślę, że jest o wiele łatwiej, jeśli użyjemy sztuczek :)

  1. Utwórz przycisk obrazu z ikoną i ustaw przezroczysty kolor tła .
  2. Umieść przycisk obrazu na EditText i coz po prawej stronie
  3. Zaimplementuj detektor onclick przycisku, aby wykonać swoją funkcję

Gotowe


1
Służy RelativeLayoutdo uzyskania właściwego pozycjonowania, po prostu wydaje się mniej skomplikowany niż inne rozwiązania i znacznie mniej kodu do utrzymania.
C0D3LIC1OU5,

12

Użycie ostatniego wkładu contains(x,y)nie zadziała bezpośrednio na wynik getBounds()(z wyjątkiem przypadku, gdy używa się „lewych” ciągów). Ta getBoundsmetoda zapewnia jedynie Rectdefiniowanie punktów elementu do rysowania znormalizowanego z początkiem na 0,0 - tak więc faktycznie musisz wykonać obliczenia matematyczne oryginalnego postu, aby dowiedzieć się, czy kliknięcie jest w obszarze rysunku w kontekście zawierający wymiary EditText, ale zmień go na górny, prawy, lewy itp. Alternatywnie możesz opisać, Rectktóry ma współrzędne w stosunku do jego położenia w EditTextkontenerze i użyj go contains(), chociaż ostatecznie robisz to samo.

Połączenie ich obu daje całkiem kompletne rozwiązanie, dodałem tylko atrybut instancji, consumesEventktóry pozwala użytkownikowi interfejsu API zdecydować, czy zdarzenie kliknięcia ma zostać przekazane, czy nie, używając jego wyniku do ustawienia ACTION_CANCEL.

Również, nie mogę zrozumieć, dlaczego boundsi actionX, actionYwartości atrybutów instancji, a nie tylko lokalny na stosie.

Oto wycinek z implementacji opartej na powyższym, którą zestawiłem. Rozwiązuje problem, który wymaga prawidłowego wykorzystania zdarzenia, zwracając wartość false. Dodaje czynnik „fuzz”. W moim przypadku użycia ikony sterowania głosem w EditTextpolu trudno mi było kliknąć, więc fuzz zwiększa efektywne granice, które uważa się za kliknięcie. Dla mnie 15działało dobrze. Potrzebowałem tylko, drawableRightżeby nie włożyć matematyki do innych, aby zaoszczędzić trochę miejsca, ale widzisz ten pomysł.

package com.example.android;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.EditText;
import android.graphics.Rect;

import com.example.android.DrawableClickListener;

public class ClickableButtonEditText extends EditText {
  public static final String LOG_TAG = "ClickableButtonEditText";

  private Drawable drawableRight;
  private Drawable drawableLeft;
  private Drawable drawableTop;
  private Drawable drawableBottom;
  private boolean consumeEvent = false;
  private int fuzz = 0;

  private DrawableClickListener clickListener;

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

  public ClickableButtonEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public ClickableButtonEditText(Context context) {
    super(context);
  }

  public void consumeEvent() {
    this.setConsumeEvent(true);
  }

  public void setConsumeEvent(boolean b) {
    this.consumeEvent = b;
  }

  public void setFuzz(int z) {
    this.fuzz = z;
  }

  public int getFuzz() {
    return fuzz;
  }

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

    if (left != null) {
      drawableLeft = left;
    }
    super.setCompoundDrawables(left, top, right, bottom);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      int x, y;
      Rect bounds;
      x = (int) event.getX();
      y = (int) event.getY();
      // this works for left since container shares 0,0 origin with bounds
      if (drawableLeft != null) {
        bounds = drawableLeft.getBounds();
        if (bounds.contains(x - fuzz, y - fuzz)) {
          clickListener.onClick(DrawableClickListener.DrawablePosition.LEFT);
          if (consumeEvent) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            return false;
          }
        }
      } else if (drawableRight != null) {
        bounds = drawableRight.getBounds();
        if (x >= (this.getRight() - bounds.width() - fuzz) && x <= (this.getRight() - this.getPaddingRight() + fuzz) 
              && y >= (this.getPaddingTop() - fuzz) && y <= (this.getHeight() - this.getPaddingBottom()) + fuzz) {

          clickListener.onClick(DrawableClickListener.DrawablePosition.RIGHT);
          if (consumeEvent) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            return false;
          }
        }
      } else if (drawableTop != null) {
        // not impl reader exercise :)
      } else if (drawableBottom != null) {
        // not impl reader exercise :)
      }
    }

    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;
  }
}

8

Rozwijając pomysł RyanM, stworzyłem bardziej elastyczną wersję, która obsługuje wszystkie typy do rysowania (góra, dół, lewa, prawa). Podczas gdy poniższy kod rozszerza TextView, dostosowanie go do EditText jest jedynie przypadkiem zamiany „rozszerza TextView” na „rozszerza EditText”. Tworzenie instancji widgetu z XML jest identyczne jak w przykładzie RyanM, z wyjątkiem nazwy widgetu.


import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;

import com.example.DrawableClickListener.DrawablePosition;

public class ButtonTextView extends TextView {

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

private int     actionX, actionY;

private DrawableClickListener clickListener;

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

public ButtonTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ButtonTextView(Context context) {
    super(context);
}

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

    if (left != null) {
        drawableLeft = left;
    }

    if (top != null) {
        drawableTop = top;
    }

    if (bottom != null) {
        drawableBottom = bottom;
    }

    super.setCompoundDrawables(left, top, right, bottom);
}

@Override
public boolean onTouchEvent(MotionEvent event) {

    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);
        }

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

        if (drawableRight != null && drawableRight.getBounds().contains(actionX, actionY)) {
            clickListener.onClick(DrawablePosition.RIGHT);
            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;
}}

DrawableClickListener jest tak prosty:

public interface DrawableClickListener {

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

A potem faktyczna implementacja:

class example implements DrawableClickListener {
public void onClick(DrawablePosition target) {
    switch (target) {
        case LEFT:
            doSomethingA();
            break;

        case RIGHT:
            doSomethingB();
            break;

        case BOTTOM:
            doSomethingC();
            break;

        case TOP:
            doSomethingD();
            break;

        default:
            break;
    }
}}

ps: Jeśli nie ustawisz detektora, dotknięcie TextView spowoduje wyjątek NullPointerException. Możesz dodać trochę więcej paranoi do kodu.


Wygląda na to, że kod nie działa, właśnie przetestowałem i nic się nie dzieje, gdy dotykam rysowalnego.
Thiago,

8

Kotlin to świetny język, w którym każdą klasę można rozszerzyć o nowe metody. Pozwala wprowadzić nową metodę dla klasy EditText, która będzie przechwytywać kliknięcia w prawo do rysowania.

fun EditText.onRightDrawableClicked(onClicked: (view: EditText) -> Unit) {
this.setOnTouchListener { v, event ->
    var hasConsumed = false
    if (v is EditText) {
        if (event.x >= v.width - v.totalPaddingRight) {
            if (event.action == MotionEvent.ACTION_UP) {
                onClicked(this)
            }
            hasConsumed = true
        }
    }
    hasConsumed
}
}

Możesz zobaczyć, że przyjmuje funkcję wywołania zwrotnego jako argument, który jest wywoływany, gdy użytkownik kliknie na prawo do rysowania.

val username = findViewById<EditText>(R.id.username_text)
    username.onRightDrawableClicked {
        it.text.clear()
    }

7

to działa dla mnie

mEditTextSearch.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if(s.length()>0){
                mEditTextSearch.setCompoundDrawablesWithIntrinsicBounds(null, null, getResources().getDrawable(android.R.drawable.ic_delete), null);
            }else{
                mEditTextSearch.setCompoundDrawablesWithIntrinsicBounds(null, null, getResources().getDrawable(R.drawable.abc_ic_search), null);
            }
        }
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
        @Override
        public void afterTextChanged(Editable s) {
        }
    });
    mEditTextSearch.setOnTouchListener(new OnTouchListener() {
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(mEditTextSearch.getCompoundDrawables()[2]!=null){
                    if(event.getX() >= (mEditTextSearch.getRight()- mEditTextSearch.getLeft() - mEditTextSearch.getCompoundDrawables()[2].getBounds().width())) {
                        mEditTextSearch.setText("");
                    }
                }
            }
            return false;
        }
    });

Musi odjąć prawą dopełnienie, jeśli istnieje, aby edytować tekst podczas określania początku trafionego prostokąta.
farid_z

4

Wiem, że to dość stare, ale ostatnio musiałem zrobić coś podobnego ... Po zobaczeniu, jak trudne jest to, wymyśliłem znacznie prostsze rozwiązanie:

  1. Utwórz układ XML zawierający EditText i obraz
  2. Podklasa FrameLayout i zawyżanie układu XML
  3. Dodaj kod dla detektora kliknięć i dowolne inne zachowanie, które chcesz

W moim przypadku potrzebowałem EditText, który miałby możliwość wyczyszczenia tekstu za pomocą przycisku. Chciałem, aby wyglądał jak SearchView, ale z wielu powodów nie chciałem korzystać z tej klasy. Poniższy przykład pokazuje, jak to osiągnąłem. Mimo że nie ma to nic wspólnego ze zmianą fokusu, zasady są takie same i pomyślałem, że bardziej korzystne byłoby opublikowanie rzeczywistego działającego kodu niż zestawienie przykładu, który może nie działać dokładnie tak, jak zamierzałem:

Oto mój układ: clearable_edit_text.xml

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <EditText
        android:id="@+id/edit_text_field"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <!-- NOTE: Visibility cannot be set to "gone" or the padding won't get set properly in code -->
    <ImageButton
        android:id="@+id/edit_text_clear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right|center_vertical"
        android:background="@drawable/ic_cancel_x"
        android:visibility="invisible"/>
</merge>

A oto klasa, która zawęża ten układ: ClearableEditText.java

public class ClearableEditText extends FrameLayout {
    private boolean mPaddingSet = false;

    /**
     * Creates a new instance of this class.
     * @param context The context used to create the instance
     */
    public ClearableEditText (final Context context) {
        this(context, null, 0);
    }

    /**
     * Creates a new instance of this class.
     * @param context The context used to create the instance
     * @param attrs The attribute set used to customize this instance
     */
    public ClearableEditText (final Context context, final AttributeSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * Creates a new instance of this class.
     * @param context The context used to create the instance
     * @param attrs The attribute set used to customize this instance
     * @param defStyle The default style to be applied to this instance
     */
    public ClearableEditText (final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);

        final LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.clearable_edit_text, this, true);
    }

    @Override
    protected void onFinishInflate () {
        super.onFinishInflate();

        final EditText editField = (EditText) findViewById(R.id.edit_text_field);
        final ImageButton clearButton = (ImageButton) findViewById(R.id.edit_text_clear);

        //Set text listener so we can show/hide the close button based on whether or not it has text
        editField.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged (final CharSequence charSequence, final int i, final int i2, final int i3) {
                //Do nothing here
            }

            @Override
            public void onTextChanged (final CharSequence charSequence, final int i, final int i2, final int i3) {
                //Do nothing here
            }

            @Override
            public void afterTextChanged (final Editable editable) {
                clearButton.setVisibility(editable.length() > 0 ? View.VISIBLE : View.INVISIBLE);
            }
        });

        //Set the click listener for the button to clear the text. The act of clearing the text will hide this button because of the
        //text listener
        clearButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick (final View view) {
                editField.setText("");
            }
        });
    }

    @Override
    protected void onLayout (final boolean changed, final int left, final int top, final int right, final int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        //Set padding here in the code so the text doesn't run into the close button. This could be done in the XML layout, but then if
        //the size of the image changes then we constantly need to tweak the padding when the image changes. This way it happens automatically
        if (!mPaddingSet) {
            final EditText editField = (EditText) findViewById(R.id.edit_text_field);
            final ImageButton clearButton = (ImageButton) findViewById(R.id.edit_text_clear);

            editField.setPadding(editField.getPaddingLeft(), editField.getPaddingTop(), clearButton.getWidth(), editField.getPaddingBottom());
            mPaddingSet = true;
        }
    }
}

Aby ta odpowiedź była bardziej zgodna z pytaniem, należy podjąć następujące kroki:

  1. Zmień zasób do rysowania na cokolwiek chcesz ... W moim przypadku był to szary X
  2. Dodaj detektor zmiany fokusa do tekstu edycji ...

3

a jeśli można wyciągnąć po lewej stronie, to ci pomoże. (dla osób pracujących z układem RTL)

 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() <= (searchbox.getLeft() + searchbox.getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width())) {
                                     // your action here

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

Jest to mieszanie pozycji bezwzględnych „getRawX” z pozycjami względnymi „getRight”. Jeśli ustawisz prawy lub lewy margines na editText, zobaczysz, jak to się psuje, gdy kliknięcie jest uruchamiane na niewłaściwych współrzędnych.
Sotti,

3

Wystarczy skopiować i wkleić poniższy kod, a to załatwi sprawę.

editMsg.setOnTouchListener(new View.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() >= (editMsg.getRight() - editMsg.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
                    // your action here

                    Toast.makeText(ChatActivity.this, "Message Sent", Toast.LENGTH_SHORT).show();
                    return true;
                }
            }
            return false;
        }
    });

1
To działało dla mnie, ale musiałem użyć getX () zamiast getRawX (). Myślę, że getRawX () działa tylko wtedy, gdy widok znajduje się na lewej krawędzi ekranu.
Glenn

1
Obliczanie pozycji jest nieprawidłowe. Mieszanie współrzędnych bezwzględnych „getRawX ()” z względnymi, takimi jak „getRight ()”
Sotti

3

Żadne z poprzednich rozwiązań nie działało dla mnie w systemie Xamarin Android . Byłem w stanie uzyskać odpowiedni detektor kliknięć rysowanych, działający w następujący sposób:

Utwórz następujący OnEditTextTouchdetektor zdarzeń:

  private void OnEditTextTouch(object sender, View.TouchEventArgs e)
    {
        var rightDrawable = _autoCompleteTextViewSearch.GetCompoundDrawables()[2];

        if (rightDrawable == null || e.Event.Action != MotionEventActions.Up)
        {
            e.Handled = false;

            return;
        }

        if (e.Event.GetX() >= _autoCompleteTextViewSearch.Width - _autoCompleteTextViewSearch.TotalPaddingRight)
        {
            // Invoke your desired action here.

            e.Handled = true;
        }

        // Forward the event along to the sender (crucial for default behaviour)
        (sender as AutoCompleteTextView)?.OnTouchEvent(e.Event);
    }

Subskrybuj wydarzenie Touch:

_autoCompleteTextViewSearch.Touch += OnEditTextTouch;

2

Wszystko jest świetne, ale dlaczego nie uczynić tego naprawdę prostym?

Stawiłem temu czoła również nie tak dawno temu ... i Android touchlistiner działa świetnie, ale daje ograniczenia w użyciu .. i wpadłem na inne rozwiązanie i mam nadzieję, że to pomoże:

    <LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/zero_row">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="match_parent">
            <ProgressBar
                android:id="@+id/loadingProgressBar"
                android:layout_gravity="center"
                android:layout_width="28dp"
                android:layout_height="28dp" />
        </LinearLayout>
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:background="@drawable/edittext_round_corners"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp">
            <ImageView
                android:layout_width="28dp"
                android:layout_height="28dp"
                app:srcCompat="@android:drawable/ic_menu_search"
                android:id="@+id/imageView2"
                android:layout_weight="0.15"
                android:layout_gravity="center|right"
                android:onClick="OnDatabaseSearchEvent" />
            <EditText
                android:minHeight="40dp"
                android:layout_marginLeft="10dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/edittext_round_corners"
                android:inputType="textPersonName"
                android:hint="Search.."
                android:textColorHint="@color/AndroidWhite"
                android:textColor="@color/AndroidWhite"
                android:ems="10"
                android:id="@+id/e_d_search"
                android:textCursorDrawable="@color/AndroidWhite"
                android:layout_weight="1" />
            <ImageView
                android:layout_width="28dp"
                android:layout_height="28dp"
                app:srcCompat="@drawable/ic_oculi_remove2"
                android:id="@+id/imageView3"
                android:layout_gravity="center|left"
                android:layout_weight="0.15"
                android:onClick="onSearchEditTextCancel" />
        </LinearLayout>

        <!--android:drawableLeft="@android:drawable/ic_menu_search"-->
        <!--android:drawableRight="@drawable/ic_oculi_remove2"-->

    </LinearLayout>

</LinearLayout>

wprowadź opis zdjęcia tutaj Teraz możesz utworzyć detektor lub zdarzenie ImageClick i robić z tekstem wszystko, co chcesz. Ten plik edittext_round_corners.xml

<item android:state_pressed="false" android:state_focused="false">
    <shape>
        <gradient
            android:centerY="0.2"
            android:startColor="@color/colorAccent"
            android:centerColor="@color/colorAccent"
            android:endColor="@color/colorAccent"
            android:angle="270"
            />
        <stroke
            android:width="0.7dp"
            android:color="@color/colorAccent" />
        <corners
            android:radius="5dp" />
    </shape>
</item>


Problem z tym podejściem polega na tym, że rozpada się, gdy tylko zaczniesz zmieniać rozmiar tekstu w EditText. Można by pomyśleć, że to tylko po stronie dewelopera, ale nie jest tak daleko, jak urządzenia mają rozmiar tekstu w ustawieniach. Możesz tego uniknąć, używając dp zamiast sp w EditText, ale tylko pogarsza. Inne problemy to między innymi obsługa wielowierszowych tekstów edycji.
Sotti,

Nigdy nie używałem tego do wyszukiwania wielu linii, więc przepraszam, nigdy nie myślałem, że ten problem może się pojawić. Prawdopodobnie pomoże zablokowanie wielu linii. Czy możesz załączyć zrzut ekranu aplikacji lub wyświetlić, aby zobaczyć, co się stanie? Spróbuję rozwiązać ten problem i może pomóc (naprawić ten kod) i mnie do wykorzystania w przyszłości. Dzięki.
Jevgenij Kononov,

Jest bardzo łatwy do replikacji, dzieje się to nawet na podglądzie układu, gdy tylko dodasz 2 linie.
Sotti,

Tło EditTextpowinno być android:background="@android:color/transparent".
CoolMind

1

Lepiej, aby ImageButton znajdował się po prawej stronie edytowanego tekstu i dawał ujemny margines układu, aby pokrywał się z edytowanym tekstem. Ustaw nasłuchiwanie na ImageButton i wykonaj operacje.


1
@Override
    public boolean onTouch(View v, MotionEvent event) {

        Drawable drawableObj = getResources().getDrawable(R.drawable.search_btn);
        int drawableWidth = drawableObj.getIntrinsicWidth();

        int x = (int) event.getX();
        int y = (int) event.getY();

        if (event != null && event.getAction() == MotionEvent.ACTION_UP) {
            if (x >= (searchPanel_search.getWidth() - drawableWidth - searchPanel_search.getPaddingRight())
                    && x <= (searchPanel_search.getWidth() - searchPanel_search.getPaddingRight())

                    && y >= searchPanel_search.getPaddingTop() && y <= (searchPanel_search.getHeight() - searchPanel_search.getPaddingBottom())) {

                getSearchData();
            }

            else {
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.showSoftInput(searchPanel_search, InputMethodManager.SHOW_FORCED);
            }
        }
        return super.onTouchEvent(event);

    }

1
<FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp" >

            <EditText
                android:id="@+id/edt_status_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="10dp"
                android:background="@drawable/txt_box_blank"
                android:ems="10"
                android:hint="@string/statusnote"
                android:paddingLeft="5dp"
                android:paddingRight="10dp"
                android:textColor="@android:color/black" />

            <Button
                android:id="@+id/note_del"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:layout_marginRight="1dp"
                android:layout_marginTop="5dp"
                android:background="@android:drawable/ic_delete" />
        </FrameLayout>

Problem z tym podejściem polega na tym, że rozpada się, gdy tylko zaczniesz zmieniać rozmiar tekstu w EditText. Można by pomyśleć, że to tylko po stronie dewelopera, ale nie jest tak daleko, jak urządzenia mają rozmiar tekstu w ustawieniach. Możesz tego uniknąć, używając dp zamiast sp w EditText, ale tylko pogarsza. Inne problemy to między innymi obsługa wielowierszowych EditTexts
Sotti

1

dla nasłuchiwacza kliknięć z lewej strony

txt.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            final int DRAWABLE_LEFT = 0;

            if (event.getAction() == MotionEvent.ACTION_UP) {
                if (event.getRawX() <= (txt
                        .getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width() +
                        txt.getPaddingLeft() +
                        txt.getLeft())) {

                          //TODO do code here
                    }
                    return true;
                }
            }
            return false;
        }
    });

Jest to mieszanie pozycji bezwzględnych „getRawX” z pozycjami względnymi „getRight”. Jeśli ustawisz prawy lub lewy margines na editText, zobaczysz, jak to się psuje, gdy kliknięcie jest uruchamiane na niewłaściwych współrzędnych.
Sotti,

1

Składane ciągnienia nie powinny być klikalne. Czystsze jest używanie osobnych widoków w poziomym LinearLayout i używanie na nich obsługi kliknięć.

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:background="@color/white"
        android:layout_marginLeft="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginRight="20dp"
        android:layout_marginEnd="20dp"
        android:layout_gravity="center_horizontal"
        android:orientation="horizontal"
        android:translationZ="4dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:background="@color/white"
            android:minWidth="40dp"
            android:scaleType="center"
            app:srcCompat="@drawable/ic_search_map"/>

        <android.support.design.widget.TextInputEditText
            android:id="@+id/search_edit"
            style="@style/EditText.Registration.Map"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:hint="@string/hint_location_search"
            android:imeOptions="actionSearch"
            android:inputType="textPostalAddress"
            android:maxLines="1"
            android:minHeight="40dp" />

        <ImageView
            android:id="@+id/location_gps_refresh"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:background="@color/white"
            android:minWidth="40dp"
            android:scaleType="center"
            app:srcCompat="@drawable/selector_ic_gps"/>
</LinearLayout>

Problem z tym podejściem polega na tym, że rozpada się, gdy tylko zaczniesz zmieniać rozmiar tekstu w EditText. Można by pomyśleć, że to tylko po stronie dewelopera, ale nie jest tak daleko, jak urządzenia mają rozmiar tekstu w ustawieniach. Możesz tego uniknąć, używając dp zamiast sp w EditText, ale tylko pogarsza. Inne problemy to między innymi obsługa wielowierszowych tekstów edycji.
Sotti,

1

Dla każdego, kto nie chce wdrożyć obsługi monstrualnych kliknięć. Możesz osiągnąć to samo za pomocą RelativeLayout. Dzięki temu masz nawet swobodną obsługę pozycjonowania wyciągu.

  <RelativeLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content">

   <android.support.design.widget.TextInputLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content">

     <android.support.design.widget.TextInputEditText
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
      />
     </android.support.design.widget.TextInputLayout>
     <ImageView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentEnd="true"
       android:layout_centerInParent="true"
       android:src="@drawable/ic_undo"/>
    </RelativeLayout>

ImageViewPozycja będzie taka sama, jak można wykorzystać drawableEnd- plus nie trzeba całą obsługę dotykowego słuchacza. Tylko odbiornik kliknięć dla ImageViewi jesteś gotowy.


Problem z tym podejściem polega na tym, że rozpada się, gdy tylko zaczniesz zmieniać rozmiar tekstu w EditText. Można by pomyśleć, że to tylko po stronie dewelopera, ale nie jest tak daleko, jak urządzenia mają rozmiar tekstu w ustawieniach. Możesz tego uniknąć, używając dp zamiast sp w EditText, ale tylko pogarsza. Inne problemy to między innymi obsługa wielowierszowych EditTexts
Sotti,

1

To działa ode mnie :) niech to również pomoże

edit_account_name.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                if (event.getRawX() >= (edit_account_name.getRight())) {
                    //clicked
                   return true;
                }
            }
            return false;
        }
    });

Jest to mieszanie pozycji bezwzględnych „getRawX” z pozycjami względnymi „getRight”. Jeśli ustawisz prawy lub lewy margines na editText, zobaczysz, jak to się psuje, gdy kliknięcie jest uruchamiane na niewłaściwych współrzędnych.
Sotti

dodałem prawy margines do edycji tekstu, mój kod nadal działa idealnie
zohaib khaliq

1

Widziałem kilka rozwiązań, ale żadne z nich mnie nie przekonało. Albo bardzo skomplikowany, albo zbyt prosty (jednorazowego użytku).

To jest obecnie moje ulubione podejście:

mEditText.setOnTouchListener(
        new OnEditTextRightDrawableTouchListener(mEditText) {
          @Override
          public void OnDrawableClick() {
            // The right drawable was clicked. Your action goes here.
          }
        });

Oto nasłuchiwanie dotykowe wielokrotnego użytku:

import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.EditText;

public abstract class OnEditTextRightDrawableTouchListener implements OnTouchListener {

  private final EditText mEditText;

  public OnEditTextRightDrawableTouchListener(@NonNull final EditText editText) {
    mEditText = editText;
  }

  @Override
  public boolean onTouch(View view, MotionEvent motionEvent) {
    if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
      final int DRAWABLE_RIGHT_POSITION = 2;
      final Drawable drawable = mEditText.getCompoundDrawables()[DRAWABLE_RIGHT_POSITION];
      if (drawable != null) {
        final float touchEventX = motionEvent.getX();
        final int touchAreaRight = mEditText.getRight();
        final int touchAreaLeft = touchAreaRight - drawable.getBounds().width();
        if (touchEventX >= touchAreaLeft && touchEventX <= touchAreaRight) {
          view.performClick();
          OnDrawableClick();
        }
        return true;
      }
    }
    return false;
  }

  public abstract void OnDrawableClick();
}

Tutaj możesz zobaczyć Gist.


1

Postępuj zgodnie z poniższym kodem, aby klikać prawym, lewym, górnym, dolnym kliknięciem:

edittextview_confirmpassword.setOnTouchListener(new View.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() >= (edittextview_confirmpassword.getRight() - edittextview_confirmpassword.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
                // your action here                    edittextview_confirmpassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
                return true;
            }
        }else{
            edittextview_confirmpassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);

        }
        return false;
    }
});

}


1

Wdrożyłem w Kotlinie

edPassword.setOnTouchListener { _, event ->
            val DRAWABLE_RIGHT = 2
            val DRAWABLE_LEFT = 0
            val DRAWABLE_TOP = 1
            val DRAWABLE_BOTTOM = 3
            if (event.action == MotionEvent.ACTION_UP) {
                if (event.rawX >= (edPassword.right - edPassword.compoundDrawables[DRAWABLE_RIGHT].bounds.width())) {
                    edPassword.setText("")
                    true
                }
            }
            false
        }

0

Oto moje rozwiązanie proste, wystarczy umieścić ImageButtonnad EditText:

<RelativeLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content">

  <EditText android:id="@+id/editTextName"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:imeOptions="actionSearch"
    android:inputType="text"/>

  <ImageButton android:id="@+id/imageViewSearch"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_action_search"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"/>

</RelativeLayout>

0

Chciałbym zaproponować sposób na wyciągnięcie lewej strony! Próbowałem tego kodu i działa.

txtsearch.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            final int DRAWABLE_LEFT = 0;
            int start=txtsearch.getSelectionStart();
            int end=txtsearch.getSelectionEnd();
            if(event.getAction() == MotionEvent.ACTION_UP) {
                if(event.getRawX() <= (txtsearch.getLeft() + txtsearch.getCompoundDrawables()[DRAWABLE_LEFT].getBounds().width())) {
                    //Do your action here
                    return true;
                }

            }
            return false;
        }
    });
}

Jest to mieszanie pozycji bezwzględnych „getRawX” z pozycjami względnymi „getRight”. Jeśli ustawisz prawy lub lewy margines na editText, zobaczysz, jak to się psuje, gdy kliknięcie jest uruchamiane na niewłaściwych współrzędnych.
Sotti,

0

Zaimplementowałem odpowiedź @aristo_sh w Mono.Droid (Xamarin), ponieważ jest to anonimowa metoda delegowania, której nie możesz zwrócić true lub false, musisz wziąć e.Event.Handled. Chowam też klawiaturę po kliknięciu

editText.Touch += (sender, e) => {
                    e.Handled = false;
                    if (e.Event.Action == MotionEventActions.Up)
                    {
                        if (e.Event.RawX >= (bibEditText.Right - (bibEditText.GetCompoundDrawables()[2]).Bounds.Width()))
                        {
                            SearchRunner();
                            InputMethodManager manager = (InputMethodManager)GetSystemService(InputMethodService);
                            manager.HideSoftInputFromWindow(editText.WindowToken, 0);
                            e.Handled = true;
                        }
                    }
                };

0

Udostępnianie mojego ogólnego rozwiązania do obsługi złożonych zdarzeń ClickView i TouchView TextView.

Najpierw potrzebujemy obsługi zdarzeń dotykowych:

/**
 * Handles compound drawable touch events.
 * Will intercept every event that happened inside (calculated) compound drawable bounds, extended by fuzz.
 * @see TextView#getCompoundDrawables()
 * @see TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)
 */
public abstract class CompoundDrawableTouchListener implements View.OnTouchListener {

    private final String LOG_TAG = "CmpDrawableTouch";

    private final int fuzz;

    public static final int LEFT = 0;
    public static final int TOP = 1;
    public static final int RIGHT = 2;
    public static final int BOTTOM = 3;
    private static final int[] DRAWABLE_INDEXES = {LEFT, TOP, RIGHT, BOTTOM};

    /**
     * Default constructor
     */
    public CompoundDrawableTouchListener() {
        this(0);
    }

    /**
     * Constructor with fuzz
     * @param fuzz desired fuzz in px
     */
    public CompoundDrawableTouchListener(int fuzz) {
        this.fuzz = fuzz;
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
        if (!(view instanceof TextView)) {
            Log.e(LOG_TAG, "attached view is not instance of TextView");
            return false;
        }

        TextView textView = (TextView) view;
        Drawable[] drawables = textView.getCompoundDrawables();
        int x = (int) event.getX();
        int y = (int) event.getY();

        for (int i : DRAWABLE_INDEXES) {
            if (drawables[i] == null) continue;
            Rect bounds = getRelativeBounds(i, drawables[i], textView);
            Rect fuzzedBounds = addFuzz(bounds);

            if (fuzzedBounds.contains(x, y)) {
                MotionEvent relativeEvent = MotionEvent.obtain(
                    event.getDownTime(),
                    event.getEventTime(),
                    event.getAction(),
                    event.getX() - bounds.left,
                    event.getY() - bounds.top,
                    event.getMetaState());
                return onDrawableTouch(view, i, bounds, relativeEvent);
            }
        }

        return false;
    }

    /**
     * Calculates compound drawable bounds relative to wrapping view
     * @param index compound drawable index
     * @param drawable the drawable
     * @param view wrapping view
     * @return {@link Rect} with relative bounds
     */
    private Rect getRelativeBounds(int index, @NonNull Drawable drawable, View view) {
        Rect drawableBounds = drawable.getBounds();
        Rect bounds = new Rect();

        switch (index) {
            case LEFT:
                bounds.offsetTo(view.getPaddingLeft(),
                    view.getHeight() / 2 - bounds.height() / 2);
                break;

            case TOP:
                bounds.offsetTo(view.getWidth() / 2 - bounds.width() / 2,
                    view.getPaddingTop());
                break;

            case RIGHT:
                bounds.offsetTo(view.getWidth() - view.getPaddingRight() - bounds.width(),
                    view.getHeight() / 2 - bounds.height() / 2);
                break;

            case BOTTOM:
                bounds.offsetTo(view.getWidth() / 2 - bounds.width() / 2,
                    view.getHeight() - view.getPaddingBottom() - bounds.height());
                break;
        }

        return bounds;
    }

    /**
     * Expands {@link Rect} by given value in every direction relative to its center
     * @param source given {@link Rect}
     * @return result {@link Rect}
     */
    private Rect addFuzz(Rect source) {
        Rect result = new Rect();
        result.left = source.left - fuzz;
        result.right = source.right + fuzz;
        result.top = source.top - fuzz;
        result.bottom = source.bottom + fuzz;
        return result;
    }

    /**
     * Compound drawable touch-event handler
     * @param v wrapping view
     * @param drawableIndex index of compound drawable which recicved the event
     * @param drawableBounds {@link Rect} with compound drawable bounds relative to wrapping view.
     * Fuzz not included
     * @param event event with coordinated relative to wrapping view - i.e. within {@code drawableBounds}.
     * If using fuzz, may return negative coordinates.
     */
    protected abstract boolean onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event);
}

Teraz możesz przetwarzać dowolne zdarzenia dotykowe na dowolnym złożonym rysunku dowolnego TextView, który Ci się podoba:

textView1.setOnTouchListener(new CompoundDrawableTouchListener() {
            @Override
            protected void onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event) {
                switch(v.getId()) {
                    case R.id.textView1:
                        switch(drawableIndex) {
                            case CompoundDrawableTouchListener.RIGHT:
                                doStuff();
                                break;
                        }
                        break;
                }
            }
        });

Jesteś zainteresowany tylko kliknięciami? Po prostu odfiltruj według akcji MotionEvent:

/**
 * Handles compound drawable click events.
 * @see TextView#getCompoundDrawables()
 * @see TextView#setCompoundDrawablesRelativeWithIntrinsicBounds(int, int, int, int)
 * @see CompoundDrawableTouchListener
 */
public abstract class CompoundDrawableClickListener extends CompoundDrawableTouchListener {

    /**
     * Default constructor
     */
    public CompoundDrawableClickListener() {
        super();
    }

     /**
     * Constructor with fuzz
     * @param fuzz desired fuzz in px
     */
    public CompoundDrawableClickListener(int fuzz) {
        super(fuzz);
    }

    @Override
    protected void onDrawableTouch(View v, int drawableIndex, Rect drawableBounds, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) onDrawableClick(v, drawableIndex);
        return true;
    }

    /**
     * Compound drawable touch-event handler
     * @param v wrapping view
     * @param drawableIndex index of compound drawable which recicved the event
     */
    protected abstract void onDrawableClick(View v, int drawableIndex);
}

Ponownie możemy z łatwością obsługiwać kliknięcia dowolnego związku rysowalnego z dowolnego TextView:

textView1.setOnTouchListener(new CompoundDrawableClickListener() {
            @Override
            protected void onDrawableClick(View v, int drawableIndex) {
                switch(v.getId()) {
                    case R.id.textView1:
                        switch(drawableIndex) {
                            case CompoundDrawableTouchListener.RIGHT:
                                doStuff();
                                break;
                        }
                        break;
                }
            }
        });

Mam nadzieję, że podobało ci się tak jak ja. Postaram się go aktualizować tutaj i w powiązanej treści, jeśli coś się zmieni.


0

Stworzyłem prostą niestandardową klasę nasłuchiwania dotykowego zamiast niestandardowego EditText

public class MyTouchListener implements View.OnTouchListener {
private EditText editText;

public MyTouchListener(EditText editText) {
    this.editText = editText;

    setupDrawable(this.editText);
}

private void setupDrawable(final EditText editText) {
    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if(s.length()>0)
                editText.setCompoundDrawablesWithIntrinsicBounds(0,0, R.drawable.clearicon,0);
            else
                editText.setCompoundDrawablesWithIntrinsicBounds(0,0, 0,0);

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });
}

@Override
public boolean onTouch(View v, MotionEvent event) {
    if(event.getAction() == MotionEvent.ACTION_UP) {
        if(editText.getCompoundDrawables()[2]!=null){
            if(event.getX() >= (editText.getRight()- editText.getLeft() - editText.getCompoundDrawables()[2].getBounds().width())) {
                editText.setText("");
            }
        }
    }
    return false;

}

}

Nie będzie można rysować, gdy EditText jest pusty. Drawable pokaże, kiedy zaczęliśmy edycję, aby wyczyścić EditText.

Możesz po prostu ustawić detektor dotyku

mEditText.setOnTouchListener (nowy MyTouchListener (mEditText));


To trochę mylące, że TouchListener obsługuje widoczność do rysowania i samą akcję. To nie jest odpowiedzialność słuchacza dotykowego, a nazwa klasy wprowadza w błąd. Podczas obliczania pozycji względnych nie jest konieczne usuwanie marginesów z równania. getRight - szerokość to zrobi.
Sotti
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.