Xử lý các sự kiện nhấp chuột trên một drawable trong EditText


238

Tôi đã thêm một hình ảnh bên phải của văn bản trong một EditText widget, sử dụng XML sau:

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

Nhưng tôi muốn xóa EditTextkhi nhấp vào hình ảnh nhúng. Tôi có thể làm cái này như thế nào?


Bản sao có thể có của stackoverflow.com/questions/13135447/
Khắc

Câu trả lời:


358

Trên thực tế, bạn không cần phải mở rộng bất kỳ lớp học. Giả sử tôi có một chỉnh sửa Chỉnh sửa. Với một 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;
    }
});

chúng tôi getRawX()bởi vì chúng tôi muốn có được vị trí cảm ứng thực tế trên màn hình, không liên quan đến cha mẹ.

Để có được bên trái nhấp

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

2
@ user2848783 làm thế nào để đặt cái này trong drawable bên trái của tôi?
Qadir Hussain

10
Làm việc sau khi thay đổi event.getRawX()thay vì event.getX()@AngeloS
Pratik Butani

4
Một lưu ý: thay thế "return false;" để "trả lại sự thật;" nếu không, sau ACTION_DOWN -> ACTION_UP sẽ không bị hủy.
tomurka

9
Nếu bạn thêm phần đệm, bạn cần tính toán phần đó cũng như getRight()phần bên phải của TextView, phần này sẽ không phải là phần có thể rút được nếu có phần đệm. Thêm - editComment.getPaddingRight()vào cuối iftuyên bố của bạn sẽ làm việc.
kassim

21
Điều này không hoạt động nếu cha mẹ của EditText không được căn chỉnh bên trái màn hình. Bạn nên sử dụng event.getX () thay vì event.getRawX () và sử dụng editText.getWidth () thay vì editText.getRight ()
Fletcher Johns

85

Rất, rất tốt, cảm ơn mọi người đã đóng góp cho cuộc thảo luận này. Vì vậy, nếu bạn không muốn đối phó với sự bất tiện của việc mở rộng lớp học, bạn có thể thực hiện các thao tác sau (chỉ thực hiện để có thể rút ra được)

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

Và đây là cách thực hiện trình nghe xương sống dựa trên câu trả lời của @ 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
Bạn nên thêm v.getLeft () vào x và v.getTop () cho y để có được vị trí chính xác.
André

3
Thật ra bạn nên thay thế v.getRight()bằng v.getWidth().
Nhanh chóng

2
lưu ý rằng yếu tố fuzz của bạn nên chia tỷ lệ với DPI, 10px trong ldpi là một cái gì đó hoàn toàn khác với 10px trong xxhdpi.
RaB

4
Fuzz để làm gì? Vui lòng làm rõ.
Юрій Мазуревич

1
Dường như fuzzcó hiệu quả làm cho khu vực có thể mở rộng hơn một chút, làm cho nó dễ dàng hơn để khai thác nhỏ có thể rút ra.
ban-geengineering

28

Hãy xem xét những điều sau đây. Nó không phải là giải pháp tao nhã nhất nhưng nó hoạt động, tôi chỉ thử nghiệm nó.

  1. Tạo một EditTextlớp tùy chỉnh 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. Thay đổi XML bố cục của bạn thành này ( com.exampletên gói dự án thực tế của bạn):

    <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. Cuối cùng, thêm điều này (hoặc một cái gì đó tương tự) vào hoạt động của bạn:

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

Tôi có thể là một chút với tính toán của giới hạn cảm ứng cho khả năng vẽ lồng nhau nhưng bạn có ý tưởng.

Tôi hi vọng cái này giúp được.


Trên thực tế, tôi đã nghe nói rằng sửa đổi MotionEvent là một thực tế không được khuyến khích, dẫn đến hành vi không xác định có thể sẽ phá vỡ trên các nền tảng khác nhau, vì vậy có lẽ một giải pháp tốt hơn có thể là stackoverflow.com/a/6235602
Giulio Piancastelli

@RyanM, mình dùng TextViewthay EditText. Tôi đã lấy mã và nếu tôi nhấp vào TextView(không phải biểu tượng mà trên bất kỳ khoảng trắng nào trên TextView), phương thức onTouchEvent(MotionEvent event)được gọi. Vì vậy, tôi có thể triển khai OnClickListenerbình thường TextViewmà không cần bất kỳ lớp học bổ sung nào nhưCustomEditText
Maksim Dmitriev

@RyanM, Thay vì sử dụng this.getRight()-rBounds.width()tại sao không sử dụng this.getMeasuredWidth() - this.getCompoundPaddingRight()? Nó sẽ không quan tâm đến phần đệm của drawable và cũng có thể thoát khỏi ràng buộc của drawable?
Vino

@RyanM làm thế nào để thay đổi hình ảnh khi chạm vào sự kiện nhấn nút chéo?
Qadir Hussain

Các phiên bản tùy chỉnh EditTextkhông hỗ trợ các tiện ích thích hợp pha màu khi sử dụng appcompat trên các thiết bị tiền kẹo. Sử dụng AppCompatEditTextlàm lớp cha của EditText tùy chỉnh của bạn
Tomask

24

Tôi đã tạo một lớp trừu tượng hữu ích DrawableClickListener thực hiện OnTouchListener .

Ngoài lớp DrawableClickListener , tôi cũng đã tạo thêm 4 lớp trừu tượng mở rộng lớp DrawableClickListener và xử lý việc nhấp vào vùng có thể vẽ cho góc phần tư chính xác.

  • LeftDrawableClickListener
  • TopDrawableClickListener
  • RightDrawableClickListener
  • BottomDrawableClickListener

Điểm cần xem xét

Một điều cần xem xét là các hình ảnh không được thay đổi kích thước nếu được thực hiện theo cách này; do đó, hình ảnh phải được thu nhỏ chính xác trước khi đưa vào res / drawable thư mục .

Nếu bạn xác định linearLayout chứa ImageViewTextView , việc điều chỉnh kích thước của hình ảnh được hiển thị sẽ dễ dàng hơn rất nhiều.


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

Nó rất đơn giản. Hãy nói rằng bạn có một drawable ở bên trái của EditText 'txtsearch'. Sau đây sẽ làm các thủ thuật.

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

Nếu bạn muốn cho drawable đúng, thay đổi câu lệnh if thành:

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

Tương tự như vậy, bạn có thể làm điều đó cho tất cả các drawable ghép.

txtsearch.getTotalPaddingTop()
txtsearch.getTotalPaddingBottom()

Cuộc gọi phương thức này trả về tất cả các phần đệm ở bên đó bao gồm mọi phần rút ra. Bạn có thể sử dụng điều này ngay cả cho TextView, Nút, v.v.

Nhấn vào đây để tham khảo từ trang web của nhà phát triển Android.


1
Tôi nghĩ rằng đây là một câu trả lời tốt, ngoại trừ phần mà sự thật được trả lại ở mọi nơi. Tôi đề nghị chỉ trả lại đúng khi sự kiện cần được tiêu thụ (cử chỉ chạm đã xảy ra ở đúng khu vực).
Bianca Daniciuc

12

Việc sử dụng đóng góp cuối cùng đó contains(x,y)sẽ không hoạt động trực tiếp trên kết quả của getBounds()(ngoại trừ, do trùng hợp ngẫu nhiên, khi sử dụng các khoản rút tiền "bên trái"). Các getBoundsphương pháp duy nhất cung cấp các Rectđiểm quy định của mục drawable bình thường với nguồn gốc ở 0,0 - vì vậy, bạn thực sự cần phải làm toán của các bài bản gốc để tìm hiểu xem các nhấp chuột là trong lĩnh vực đối tượng vẽ trong bối cảnh của chứa kích thước của EditText, nhưng thay đổi kích thước cho trên cùng, bên phải, bên trái, v.v. Hoặc bạn có thể mô tả một Recttọa độ thực sự có liên quan đến vị trí của nó trong vùng EditTextchứa và sử dụngcontains() , mặc dù cuối cùng bạn đang thực hiện cùng một phép toán.

Kết hợp cả hai mang lại cho bạn một giải pháp khá hoàn chỉnh, tôi chỉ thêm một thuộc tính cá thể consumesEventcho phép người dùng API quyết định xem sự kiện nhấp có được truyền hay không bằng cách sử dụng kết quả của nó để đặt ACTION_CANCELhay không.

Ngoài ra, tôi không thể thấy lý do tại sao boundsactionX, actionYcác giá trị là thuộc tính cá thể thay vì chỉ cục bộ trên ngăn xếp.

Đây là một phần cắt ra từ một triển khai dựa trên những điều trên mà tôi kết hợp lại. Nó khắc phục một vấn đề là để tiêu thụ đúng sự kiện bạn cần trả về false. Nó thêm một yếu tố "fuzz". Trong trường hợp sử dụng biểu tượng Điều khiển giọng nói của tôi trong một EditTexttrường, tôi thấy khó bấm, do đó, fuzz làm tăng giới hạn hiệu quả được coi là nhấp vào có thể rút được. Đối với tôi 15làm việc tốt. Tôi chỉ cần drawableRightvì vậy tôi đã không cắm toán học vào những người khác, để tiết kiệm không gian, nhưng bạn thấy ý tưởng đó.

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

11

Tôi nghĩ sẽ dễ dàng hơn nhiều nếu chúng ta sử dụng một số thủ thuật :)

  1. Tạo nút hình ảnh với biểu tượng của bạn và đặt màu nền của nó trong suốt .
  2. Đặt nút hình ảnh trên EditText và của bên phải
  3. Thực hiện trình nghe onclick của nút để thực thi chức năng của bạn

Làm xong


1
Được sử dụng RelativeLayoutđể đạt được vị trí thích hợp, dường như ít phức tạp hơn các giải pháp khác và ít mã hơn để duy trì.
C0D3LIC1OU5

8

Kotlin là một ngôn ngữ tuyệt vời nơi mỗi lớp có thể được mở rộng bằng các phương thức mới. Cho phép giới thiệu phương thức mới cho lớp EditText, sẽ bắt các nhấp chuột để có thể vẽ được.

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

Bạn có thể thấy nó có chức năng gọi lại làm đối số được gọi khi người dùng nhấp vào bên phải.

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

7

Mở rộng ý tưởng của RyanM Tôi đã tạo ra một phiên bản linh hoạt hơn, hỗ trợ tất cả các loại có thể vẽ được (trên, dưới, trái, phải). Mặc dù đoạn mã bên dưới mở rộng TextView, việc điều chỉnh nó cho EditText chỉ là một trường hợp hoán đổi "mở rộng TextView" bằng "extends EditText". Khởi tạo widget từ XML giống hệt như trong ví dụ của RyanM, chặn tên widget.


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 đơn giản như thế này:

public interface DrawableClickListener {

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

Và sau đó thực hiện thực tế:

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: Nếu bạn không đặt trình nghe, chạm vào TextView sẽ gây ra NullPulumException. Bạn có thể muốn thêm một số hoang tưởng vào mã.


Có vẻ như mã của bạn không hoạt động, tôi vừa kiểm tra và không có gì xảy ra khi tôi chạm vào drawable.
Thiago

7

nó làm việc cho tôi,

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

Cần phải trừ phần đệm bên phải, nếu có, để chỉnh sửa văn bản khi xác định bắt đầu hình chữ nhật nhấn.
farid_z

4

Tôi biết điều này khá cũ, nhưng gần đây tôi đã phải làm một cái gì đó tương tự ... Sau khi thấy điều này khó khăn như thế nào, tôi đã đưa ra một giải pháp đơn giản hơn nhiều:

  1. Tạo bố cục XML có chứa EditText và Image
  2. Phân lớp FrameLayout và tăng bố cục XML
  3. Thêm mã cho người nghe nhấp chuột và bất kỳ hành vi nào bạn muốn

Trong trường hợp của tôi, tôi cần một EditText có khả năng xóa văn bản bằng một nút. Tôi muốn nó trông giống như SearchView, nhưng vì nhiều lý do tôi không muốn sử dụng lớp đó. Ví dụ dưới đây cho thấy cách tôi hoàn thành việc này. Mặc dù nó không liên quan đến thay đổi tiêu điểm, các nguyên tắc là như nhau và tôi cho rằng việc đăng mã làm việc thực tế sẽ có ích hơn là đưa ra một ví dụ có thể không hoạt động chính xác như tôi dự định:

Đây là cách bố trí của tôi: cleaabilities_editSphere.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>

Và đây là Class thổi phồng bố cục đó: CleaabilitiesEditText.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;
        }
    }
}

Để làm cho câu trả lời này phù hợp hơn với câu hỏi, cần thực hiện các bước sau:

  1. Thay đổi tài nguyên có thể rút thành bất cứ thứ gì bạn muốn ... Trong trường hợp của tôi, đó là màu X màu xám
  2. Thêm một người nghe thay đổi tập trung vào văn bản chỉnh sửa ...

3

và nếu drawable ở bên trái, điều này sẽ giúp bạn. (đối với những người làm việc với bố trí 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;
        }
    });

Điều này là trộn các vị trí tuyệt đối "getRawX" với các vị trí tương đối "getRight". Nếu bạn đặt lề phải hoặc trái trên editText, bạn sẽ thấy cách ngắt này khi nhấp được kích hoạt trên tọa độ sai.
Sotti

3

Đơn giản chỉ cần sao chép dán mã sau đây và nó thực hiện các mẹo.

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
Điều này làm việc cho tôi nhưng tôi phải sử dụng getX () thay vì getRawX (). Tôi nghĩ getRawX () chỉ hoạt động nếu chế độ xem nằm ở cạnh trái của màn hình.
Glenn

1
Việc tính toán các vị trí là sai. Đang trộn các tọa độ tuyệt đối "getRawX ()", với các tọa độ tương đối như "getRight ()"
Sotti

3

Không có giải pháp nào trước đây hoạt động với tôi trong Xamarin Android . Tôi đã có thể có được trình nghe nhấp chuột có thể vẽ đúng hoạt động bằng cách sử dụng như sau:

Tạo trình OnEditTextTouchnghe sự kiện sau :

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

Theo dõi sự kiện Touch:

_autoCompleteTextViewSearch.Touch += OnEditTextTouch;

2

Đó là tất cả tuyệt vời nhưng tại sao không làm cho nó thực sự đơn giản?

Tôi đã phải đối mặt với điều đó cách đây không lâu ... và android touchlistiner hoạt động rất tốt nhưng hạn chế trong cách sử dụng..và tôi đã đến một giải pháp khác và tôi hy vọng điều đó sẽ giúp bạn:

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

nhập mô tả hình ảnh ở đây Bây giờ bạn có thể tạo trình nghe hoặc sự kiện ImageClick và làm những gì bạn muốn với văn bản. Tệp edittext_round_corners.xml này

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


Vấn đề với cách tiếp cận này là sự sụp đổ ngay khi bạn bắt đầu thay đổi kích thước văn bản trên EditText. Bạn có thể nghĩ rằng đó chỉ là về phía nhà phát triển nhưng nó không xa như các thiết bị có kích thước văn bản trong cài đặt. Bạn có thể tránh điều này bằng cách sử dụng dp thay vì sp trên EditText nhưng nó chỉ làm mọi thứ tồi tệ hơn. Các vấn đề khác là những thứ như xử lý EditTexts đa dòng.
Sotti

Tôi chưa bao giờ sử dụng nó cho tìm kiếm nhiều dòng, vì vậy xin lỗi tôi không bao giờ nghĩ rằng vấn đề này có thể xuất hiện. Có lẽ chặn cho nhiều dòng sẽ giúp. Bạn có thể đính kèm ảnh chụp màn hình của ứng dụng hoặc xem để xem điều gì xảy ra không? Và tôi sẽ cố gắng giải quyết điều đó và có thể giúp bạn (sửa mã này) và tôi để sử dụng trong tương lai. Cảm ơn.
Jevgenij Kononov

Thật dễ dàng để sao chép, nó thậm chí còn xảy ra trên bản xem trước bố cục ngay khi bạn thêm 2 dòng.
Sotti

Một nền tảng cho EditTextnên được android:background="@android:color/transparent".
CoolMind

1

Tốt hơn là có ImageButton ở bên phải chỉnh sửa văn bản và đưa lề bố cục âm để chồng lấp với văn bản chỉnh sửa. Đặt trình nghe trên ImageButton và thực hiện các thao tác.


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>

Vấn đề với cách tiếp cận này là sự sụp đổ ngay khi bạn bắt đầu thay đổi kích thước văn bản trên EditText. Bạn có thể nghĩ rằng đó chỉ là về phía nhà phát triển nhưng nó không xa như các thiết bị có kích thước văn bản trong cài đặt. Bạn có thể tránh điều này bằng cách sử dụng dp thay vì sp trên EditText nhưng nó chỉ làm mọi thứ tồi tệ hơn. Các vấn đề khác là những vấn đề như xử lý Multiline EditTexts
Sotti 17/12/17

1

cho người nghe nhấp chuột trái

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

Điều này là trộn các vị trí tuyệt đối "getRawX" với các vị trí tương đối "getRight". Nếu bạn đặt lề phải hoặc trái trên editText, bạn sẽ thấy cách ngắt này khi nhấp được kích hoạt trên tọa độ sai.
Sotti

1

Drawable hợp chất không được cho là có thể nhấp. Sẽ sạch hơn khi sử dụng các chế độ xem riêng biệt trong linearLayout ngang và sử dụng trình xử lý nhấp chuột trên chúng.

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

Vấn đề với cách tiếp cận này là sự sụp đổ ngay khi bạn bắt đầu thay đổi kích thước văn bản trên EditText. Bạn có thể nghĩ rằng đó chỉ là về phía nhà phát triển nhưng nó không xa như các thiết bị có kích thước văn bản trong cài đặt. Bạn có thể tránh điều này bằng cách sử dụng dp thay vì sp trên EditText nhưng nó chỉ làm mọi thứ tồi tệ hơn. Các vấn đề khác là những thứ như xử lý EditTexts đa dòng.
Sotti

1

Đối với bất cứ ai không muốn thực hiện xử lý nhấp chuột quái dị. Bạn có thể đạt được điều tương tự với a RelativeLayout. Với điều đó, bạn thậm chí có thể xử lý miễn phí vị trí của drawable.

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

Vị ImageViewtrí sẽ giống như bạn sẽ sử dụng drawableEnd- cộng với bạn không cần tất cả xử lý người nghe cảm ứng. Chỉ cần một người nghe nhấp chuột cho ImageViewvà bạn là tốt để đi.


Vấn đề với cách tiếp cận này là sự sụp đổ ngay khi bạn bắt đầu thay đổi kích thước văn bản trên EditText. Bạn có thể nghĩ rằng đó chỉ là về phía nhà phát triển nhưng nó không xa như các thiết bị có kích thước văn bản trong cài đặt. Bạn có thể tránh điều này bằng cách sử dụng dp thay vì sp trên EditText nhưng nó chỉ làm mọi thứ tồi tệ hơn. Các vấn đề khác là những vấn đề như xử lý Multiline EditTexts
Sotti 17/12/17

1

Điều này hoạt động với tôi :) điều này có thể giúp bạn là tốt

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

Điều này là trộn các vị trí tuyệt đối "getRawX" với các vị trí tương đối "getRight". Nếu bạn đặt lề phải hoặc trái trên editText, bạn sẽ thấy cách ngắt này khi nhấp được kích hoạt trên tọa độ sai.
Sotti

tôi đã thêm lề phải vào văn bản chỉnh sửa, mã của tôi vẫn hoạt động hoàn hảo
zohaib khaliq

1

Tôi đã thấy một số giải pháp nhưng tôi đã không bị thuyết phục bởi bất kỳ trong số họ. Hoặc rất phức tạp hoặc quá đơn giản (không thể tái sử dụng).

Đây là cách tiếp cận yêu thích của tôi tại thời điểm này:

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

Và đây là trình nghe cảm ứng có thể tái sử dụng:

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

Bạn có thể nhìn vào Gist ở đây.


1

Thực hiện theo mã dưới đây để có thể vẽ phải, trái, lên, xuống nhấp:

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

Tôi đã thực hiện ở Kotlin

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

Đây là giải pháp đơn giản của tôi, chỉ cần đặt ImageButtonqua 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

Tôi muốn đề xuất một cách cho drawable trái! Tôi đã thử mã này và làm việc.

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

Điều này là trộn các vị trí tuyệt đối "getRawX" với các vị trí tương đối "getRight". Nếu bạn đặt lề phải hoặc trái trên editText, bạn sẽ thấy cách ngắt này khi nhấp được kích hoạt trên tọa độ sai.
Sotti

0

Tôi đã triển khai câu trả lời @aristo_sh trong Mono.Droid (Xamarin), vì đó là phương pháp ẩn danh dành cho đại biểu mà bạn không thể trả về đúng hay sai mà bạn phải thực hiện với e.Event.Handled. Tôi cũng đang ẩn bàn phím khi nhấp

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

Chia sẻ giải pháp tổng quát của tôi để xử lý các sự kiện nhấp và chạm có thể vẽ được của TextView.

Đầu tiên chúng ta cần một trình xử lý sự kiện cảm ứng:

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

Giờ đây, bạn có thể xử lý bất kỳ sự kiện chạm nào trên bất kỳ hợp chất có thể vẽ nào của bất kỳ TextView nào bạn thích theo cách này:

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

Chỉ quan tâm đến nhấp chuột? Chỉ cần lọc ra bằng hành động 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);
}

Một lần nữa, chúng ta có thể dễ dàng xử lý các nhấp chuột trên bất kỳ hợp chất có thể rút ra của bất kỳ TextView nào:

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

Hy vọng bạn thích nó như tôi đã làm. Tôi sẽ cố gắng giữ cho nó được cập nhật ở đây và trong ý chính liên quan nếu có gì thay đổi.


0

Tôi đã tạo một lớp trình nghe cảm ứng tùy chỉnh đơn giản thay vì EditText tùy chỉnh

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;

}

}

Sẽ không thể rút ra được khi EditText trống. Một drawable sẽ hiển thị khi chúng tôi bắt đầu chỉnh sửa để xóa EditText.

Bạn chỉ có thể đặt trình nghe cảm ứng

mEditText.setOnTouchListener (MyTouchListener mới (mEditText));


Có một chút khó hiểu khi TouchListener đang xử lý khả năng hiển thị có thể vẽ và chính hành động rõ ràng. Đó không phải là trách nhiệm của người nghe và tên của lớp là sai lệch. Cũng như bạn đang tính toán các vị trí tương đối là không cần thiết để loại bỏ lề khỏi phương trình. getRight - chiều rộng sẽ làm điều đó.
Sotti
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.