最近工做中須要一個能夠刪除全部字符的EditText,因此本身寫了個自定義view繼承Edittext,這個實現相對簡單,只用到了自定義view中的部分事件。
首先咱們來看一下效果,是怎麼樣的:javascript
從途中能夠看到總共分爲兩個部分,一個是標準的EditText,另外一個是右邊的咱們自定義的圖標,在未輸入字符以前,圖標是隱藏的,輸入字符後,圖標顯示,點擊圖標便可刪除EditText中的全部文字,同時隱藏圖標。java
首先咱們須要編寫一個類繼承自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軸的中心位置。
咱們實現自定義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中一些基本流程,要深刻進去須要掌握的遠遠多於這些,下一節將繼續咱們的學習。