自定義view之寫一個帶刪除按鈕的Edittext

自定義EditText的需求:

最近工做中須要一個能夠刪除全部字符的EditText,因此本身寫了個自定義view繼承Edittext,這個實現相對簡單,只用到了自定義view中的部分事件。
首先咱們來看一下效果,是怎麼樣的:javascript

從途中能夠看到總共分爲兩個部分,一個是標準的EditText,另外一個是右邊的咱們自定義的圖標,在未輸入字符以前,圖標是隱藏的,輸入字符後,圖標顯示,點擊圖標便可刪除EditText中的全部文字,同時隱藏圖標。java

繼承EditText

首先咱們須要編寫一個類繼承自EditText:android

public class ClearEditText extends android.support.v7.widget.AppCompatEditText{

    public ClearEditText(Context context) {
        this(context, null);
    }

    public ClearEditText(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.editTextStyle);
    }

    public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
       init();
    }
    private void init() {
        Log.d("順序", "init");
        }
}複製代碼

繼承EditText必須實現其中的構造方法,此處咱們重寫了三個,事實上只要一個便可,不定義屬性時會默認設置爲號爲editTextStyle屬性集。
咱們都知道自定義view時候一般會重寫onDraw和onMeasure方法,那麼這幾個方法究竟是按怎樣的順序執行呢,咱們能夠在代碼中添加測試代碼來實驗一下:canvas

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("順序", "onMeasure");
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 @Override
    protected void onDraw(Canvas canvas) {
        Log.d("順序", "draw");
        super.onDraw(canvas);
    }複製代碼

而後咱們在一個fragment中加載這個view,輸出日誌api

03-07 14:51:10.805 14606-14606/com.saka.customviewdemo D/順序: init
03-07 14:51:10.820 14606-14606/com.saka.customviewdemo D/順序: onMeasure
03-07 14:51:10.880 14606-14606/com.saka.customviewdemo D/順序: draw複製代碼

能夠看到執行的順序是按構造器—>onmeasure->onDraw來執行的。數組

設置圖標

最簡單的方法是讓ui切圖,切出不一樣的分辨率,放在drawable中,直接調用。ide

此處我沒有UI,我也不擅長PS,因此我用xml作了一個簡單的刪除按鈕。函數

首先建立一個vector類型的drawableresource佈局

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="16dp"
    android:height="16dp"
    android:viewportHeight="16"
    android:viewportWidth="16">
    <path
        android:pathData="M 0 8 L 16 8"
        android:strokeColor="#2c2c2c"
        android:strokeWidth="3" />
    <path
        android:pathData="M 8 0 L 8 16"
        android:strokeColor="#2c2c2c"
        android:strokeWidth="3" />

</vector>複製代碼

這個圖標是正方形,邊長是16dp線條寬度3dp,而後作了一個十字型,大概是這個樣子學習

而後再自定義一個rotate類型的drawableresource,這個也就是咱們要使用的圖標資源:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/delete" android:fromDegrees="0" android:toDegrees="225" android:pivotX="50%" android:pivotY="50%"> </rotate>複製代碼

這個rotaterawable設置了旋轉角度是從0轉到225度,旋轉的中心位置在圖片X軸和Y軸的中心位置。

添加Drawable

咱們實現自定義EditText的思路是佔用右邊的drawable位置,點擊這個drawable便可觸發清除字符事件。

此處咱們是在構造器中添加的Drawable,經過init()方法來加載右側圖標。那麼怎樣獲取這個位置呢?

首先我來看一下繼承關係

java.lang.Object
   ↳     android.view.View
         ↳     android.widget.TextView
               ↳     android.widget.EditText複製代碼

在TextView中有這樣一個屬性:android:drawableRight,這個屬性是設置控件右邊的圖標。這個屬性對應的java代碼是setCompoundDrawablesWithIntrinsicBounds(int,int,int,int),這三個int值的順序對應的位置是左上右下,其中第三個位置就是drawableright屬性。此處應該注意,假如xml佈局中設置了drawableright屬性,同時java代碼中設置了setCompoundDrawablesWithIntrinsicBounds(null,null,null,null),則java代碼中的設置會覆蓋xml佈局中的設置。

既然能設置咱們就有辦法獲取每個圖標。經過查看api咱們找到一個方法,Drawable[] getCompoundDrawables (),注意這個方法返回的是一個drawable數組,長度是4,對應的圖標位置是左上右下,即便你沒有設置任何drawable,這時的四個值都爲null。
另外一個方法Drawable[] getCompoundDrawablesRelative ()返回的也是一個數組,長度一樣是4,對應的圖標位置是start,top,end和bottom,注意和上面的方法區分。

此處爲了簡單起見,咱們直接在代碼中設置右側圖標,覆蓋xml佈局中的設置,同時設置圖標不可見。

private RotateDrawable drawableRotate;

 private void init() {
        Log.d("順序", "init");        
        setIconVisible(false, getCompoundDrawables());
    }

    private void setIconVisible(boolean b, Drawable[] drawables) {
        if (b) {            
            setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], getResources().getDrawable(R.drawable.mydelete), drawables[3]);
            drawableRotate = (RotateDrawable) getCompoundDrawables()[2];
        } else {
            setCompoundDrawablesWithIntrinsicBounds(drawables[0], drawables[1], null, drawables[3]);
        }
    }複製代碼

這樣,咱們的圖標就引入了EditText中,只是它如今是隱藏的,咱們沒法看到他。

設置圖標可見與不可見

咱們的目標是在有文字時顯示圖標,沒有文字時隱藏圖標,這個時候咱們最好的方法是實現TextWatcher方法,它一共有三個

public void beforeTextChanged(CharSequence s, int start,
                                  int count, int after);
public void onTextChanged(CharSequence s, int start, int before, int count);

public void afterTextChanged(Editable s);複製代碼

咱們重點關注第二個方法,這個方法在更改s的時候會用這個回調來通知你,在s中,從start位置開始的count個字符剛剛替換了before開始的的舊文本。
這裏咱們就能夠利用這幾個參數來計算此時的狀態:

@Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (s.length() == 0 && before > 0) {
            //從有文字刪除到無文字的時候
            startAnimatorSetResver();
            return;
        }
        if (start == 0 && s.length() > 0) {
            //從無文字到有文字
            setIconVisible(true, getCompoundDrawables());
            setAnimator();
            startAnimatorSet();
        }
    }複製代碼

註解中已經說明了兩個方法的做用,動畫函數稍後講。此時試試你就能夠顯示和隱藏圖標了。

添加消失和出現的動畫

private ValueAnimator alphaAnimator = ValueAnimator.ofInt(0, 255);
private ValueAnimator rotateAnimator = ValueAnimator.ofInt(0, 10000);複製代碼

此處咱們設置了兩個動畫,一個是用來設置通明度變化,一個是用來設置旋轉角度的。drawable有兩個屬性能夠用來設置,一個是setLevel(),這個level就是設置的旋轉角度,範圍是1-10000(假如你是用的是ScaleDrawable,這個level控制就是你的圖片的大小)。另外一個就是setAlpha(),這個alpha就是透明度,範圍是0-255。

private void setAnimator() {
        alphaAnimator.setDuration(1000);
        alphaAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                drawableRotate.setAlpha((Integer) animation.getAnimatedValue());
            }
        });

        rotateAnimator.setDuration(1000);
        rotateAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                drawableRotate.setLevel((Integer) animation.getAnimatedValue());
            }
        });
    }複製代碼

而後咱們定義一個同時啓動動畫的集合:

private void startAnimatorSet() {
        AnimatorSet setVisible = new AnimatorSet();
        setVisible.playTogether(alphaAnimator, rotateAnimator);
        setVisible.start();
    }複製代碼

這樣,咱們設置圖片顯示的動畫就完成了。當你輸入字符時,就能夠看到圖標慢慢旋轉出現了。

同理能夠設置圖標消失的動畫,不詳細寫出了,能夠看demo(個人代碼水平有點懶,沒有優化)。

設置點擊事件

其實咱們此處並非真正的設置點擊事件,而是經過判斷用戶的觸摸行爲來模擬點擊事件:

@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

            case MotionEvent.ACTION_UP:
                if (getCompoundDrawables()[2] != null) {
                    if (getWidth() - getTotalPaddingRight() < event.getX() &&
                            getWidth() - getPaddingRight() > event.getX()) {
                        this.setText("");
                        Log.d("點擊了圖片", "圖片");
                    }
                }
                break;
        }
        return super.onTouchEvent(event);
    }複製代碼

咱們並非重寫onTouchEvent事件,咱們只是在onTouchEvent中添加了一個在手指離開屏幕時是否正在圖片上的判斷,而後將內容設置爲空,通過次操做之後,繼續原有的onTouchEvent流程。

判斷手指離開屏幕的位置的方法是這樣的,api中有這樣的方法:getWidth返回的是控件的寬度,getTotalPadingRight返回的是空間右邊的padding,包含了drawable,getPaddingRight返回的是view右邊的padding,要是包含滾動條,滾動條的寬度也在pading內。

至此咱們的自定義EditText就完成了,可使用。這篇文章只是簡單的降解了一下自定義view中一些基本流程,要深刻進去須要掌握的遠遠多於這些,下一節將繼續咱們的學習。

相關文章
相關標籤/搜索