Android
開發中,EditText的使用 很是常見SuperEditText
控件,但願大家會喜歡。已在
Github
開源:Super_EditText,歡迎Star
!android
一款 附帶一鍵刪除功能 & 自定義樣式豐富的 SuperEditText
控件git
已在
Github
開源:Super_EditText,歡迎Star
!github
對於 EditText
來講,通常的需求有:canvas
UI
元素根據需求場景,得出EditText
須要具有的功能以下:bash
注:該樣式的設置是系統自帶的 API
所不具有的微信
對比市面上EditText
控件,該控件Super_EditText 的特色是:ide
EditText
使用需求可自定義樣式以下:(注:該樣式的設置是系統自帶的
API
所不具有的)源碼分析
xml
屬性配置Github
上開源:Super_EditText因此,在其上作二次開發 & 定製化成本很是低。字體
下面將給出詳細的功能邏輯ui
/*
* 步驟1:定義屬性
* */
private int ic_deleteResID; // 刪除圖標 資源ID
private Drawable ic_delete; // 刪除圖標
private int delete_x,delete_y,delete_width,delete_height; // 刪除圖標起點(x,y)、刪除圖標寬、高(px)
/*
* 步驟2:初始化屬性
* */
private void init(Context context, AttributeSet attrs) {
// 獲取控件資源
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SuperEditText);
/**
* 初始化刪除圖標
*/
// 1. 獲取資源ID
ic_deleteResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_delete,R.drawable.delete);
// 2. 根據資源ID獲取圖標資源(轉化成Drawable對象)
ic_delete = getResources().getDrawable(ic_deleteResID);
// 3. 設置圖標大小
// 起點(x,y)、寬= left_width、高 = left_height
delete_x = typedArray.getInteger(R.styleable.SuperEditText_delete_x, 0);
delete_y = typedArray.getInteger(R.styleable.SuperEditText_delete_y, 0);
delete_width = typedArray.getInteger(R.styleable.SuperEditText_delete_width, 60);
delete_height = typedArray.getInteger(R.styleable.SuperEditText_delete_height, 60);
ic_delete.setBounds(delete_x, delete_y, delete_width, delete_height);
/**
* 步驟3:經過監聽複寫EditText自己的方法來肯定是否顯示刪除圖標
* 監聽方法:onTextChanged() & onFocusChanged()
* 調用時刻:當輸入框內容變化時 & 焦點發生變化時
*/
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
setDeleteIconVisible(hasFocus() && text.length() > 0,hasFocus());
// hasFocus()返回是否得到EditTEXT的焦點,便是否選中
// setDeleteIconVisible() = 根據傳入的是否選中 & 是否有輸入來判斷是否顯示刪除圖標->>關注1
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
setDeleteIconVisible(focused && length() > 0,focused);
// focused = 是否得到焦點
// 一樣根據setDeleteIconVisible()判斷是否要顯示刪除圖標->>關注1
}
/**
* 關注1
* 做用:判斷是否顯示刪除圖標
*/
private void setDeleteIconVisible(boolean deleteVisible,boolean leftVisible) {
setCompoundDrawables(leftVisible ? ic_left_click : ic_left_unclick, null,
deleteVisible ? ic_delete: null, null);
// setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)介紹
// 做用:在EditText上、下、左、右設置圖標(至關於android:drawableLeft="" android:drawableRight="")
// 備註:傳入的Drawable對象必須已經setBounds(x,y,width,height),即必須設置過初始位置、寬和高等信息
// x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
// 若不想在某個地方顯示,則設置爲null
// 另一個類似的方法:setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)
// 做用:在EditText上、下、左、右設置圖標
// 與setCompoundDrawables的區別:setCompoundDrawablesWithIntrinsicBounds()傳入的Drawable的寬高=固有寬高(自動經過getIntrinsicWidth()& getIntrinsicHeight()獲取)
// 不須要設置setBounds(x,y,width,height)
}
/**
* 步驟4:對刪除圖標區域設置點擊事件,即"點擊 = 清空搜索框內容"
* 原理:當手指擡起的位置在刪除圖標的區域,即視爲點擊了刪除圖標 = 清空搜索框內容
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
// 原理:當手指擡起的位置在刪除圖標的區域,即視爲點擊了刪除圖標 = 清空搜索框內容
switch (event.getAction()) {
// 判斷動做 = 手指擡起時
case MotionEvent.ACTION_UP:
Drawable drawable = ic_delete;
if (drawable != null && event.getX() <= (getWidth() - getPaddingRight())
&& event.getX() >= (getWidth() - getPaddingRight() - drawable.getBounds().width())) {
// 判斷條件說明
// event.getX() :擡起時的位置座標
// getWidth():控件的寬度
// getPaddingRight():刪除圖標圖標右邊緣至EditText控件右邊緣的距離
// 即:getWidth() - getPaddingRight() = 刪除圖標的右邊緣座標 = X1
// getWidth() - getPaddingRight() - drawable.getBounds().width() = 刪除圖標左邊緣的座標 = X2
// 因此X1與X2之間的區域 = 刪除圖標的區域
// 當手指擡起的位置在刪除圖標的區域(X2=<event.getX() <=X1),即視爲點擊了刪除圖標 = 清空搜索框內容
setText("");
}
break;
}
return super.onTouchEvent(event);
}
複製代碼
/*
* 步驟1:定義屬性
* */
private Paint mPaint; // 畫筆
private int ic_left_clickResID,ic_left_unclickResID; // 左側圖標 資源ID(點擊 & 無點擊)
private Drawable ic_left_click,ic_left_unclick; // 左側圖標(點擊 & 未點擊)
private int left_x,left_y,left_width,left_height; // 左側圖標起點(x,y)、左側圖標寬、高(px)
private int cursor; // 光標
// 分割線變量
private int lineColor_click,lineColor_unclick;// 點擊時 & 未點擊顏色
private int color;
private int linePosition; // 分割線位置
/*
* 步驟2:初始化屬性
* */
private void init(Context context, AttributeSet attrs) {
// 獲取控件資源
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SuperEditText);
/**
* 初始化左側圖標(點擊 & 未點擊)
*/
// a. 點擊狀態的左側圖標
// 1. 獲取資源ID
ic_left_clickResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_left_click, R.drawable.ic_left_click);
// 2. 根據資源ID獲取圖標資源(轉化成Drawable對象)
ic_left_click = getResources().getDrawable(ic_left_clickResID);
// 3. 設置圖標大小
// 起點(x,y)、寬= left_width、高 = left_height
left_x = typedArray.getInteger(R.styleable.SuperEditText_left_x, 0);
left_y = typedArray.getInteger(R.styleable.SuperEditText_left_y, 0);
left_width = typedArray.getInteger(R.styleable.SuperEditText_left_width, 60);
left_height = typedArray.getInteger(R.styleable.SuperEditText_left_height, 60);
ic_left_click.setBounds(left_x, left_y,left_width, left_height);
// Drawable.setBounds(x,y,width,height) = 設置Drawable的初始位置、寬和高等信息
// x = 組件在容器X軸上的起點、y = 組件在容器Y軸上的起點、width=組件的長度、height = 組件的高度
// b. 未點擊狀態的左側圖標
// 1. 獲取資源ID
ic_left_unclickResID = typedArray.getResourceId(R.styleable.SuperEditText_ic_left_unclick, R.drawable.ic_left_unclick);
// 2. 根據資源ID獲取圖標資源(轉化成Drawable對象)
// 3. 設置圖標大小(此處默認左側圖標點解 & 未點擊狀態的大小相同)
ic_left_unclick = getResources().getDrawable(ic_left_unclickResID);
ic_left_unclick.setBounds(left_x, left_y,left_width, left_height);
/**
* 設置EditText左側圖片(初始狀態僅有左側圖片))
*/
setCompoundDrawables( ic_left_unclick, null,
null, null);
// setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)介紹
// 做用:在EditText上、下、左、右設置圖標(至關於android:drawableLeft="" android:drawableRight="")
// 備註:傳入的Drawable對象必須已經setBounds(x,y,width,height),即必須設置過初始位置、寬和高等信息
// x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
// 若不想在某個地方顯示,則設置爲null
// 另一個類似的方法:setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)
// 做用:在EditText上、下、左、右設置圖標
// 與setCompoundDrawables的區別:setCompoundDrawablesWithIntrinsicBounds()傳入的Drawable的寬高=固有寬高(自動經過getIntrinsicWidth()& getIntrinsicHeight()獲取)
// 不須要設置setBounds(x,y,width,height)
/**
* 初始化光標(顏色 & 粗細)
*/
// 原理:經過 反射機制 動態設置光標
// 1. 獲取資源ID
cursor = typedArray.getResourceId(R.styleable.SuperEditText_cursor, R.drawable.cursor);
try {
// 2. 經過反射 獲取光標屬性
Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
f.setAccessible(true);
// 3. 傳入資源ID
f.set(this, cursor);
} catch (Exception e) {
e.printStackTrace();
}
/**
* 初始化分割線(顏色、粗細、位置)
*/
// 1. 設置畫筆
mPaint = new Paint();
mPaint.setStrokeWidth(2.0f); // 分割線粗細
// 2. 設置分割線顏色(使用十六進制代碼,如#33三、#8e8e8e)
int lineColorClick_default = context.getResources().getColor(R.color.lineColor_click); // 默認 = 藍色#1296db
int lineColorunClick_default = context.getResources().getColor(R.color.lineColor_unclick); // 默認 = 灰色#9b9b9b
lineColor_click = typedArray.getColor(R.styleable.SuperEditText_lineColor_click, lineColorClick_default);
lineColor_unclick = typedArray.getColor(R.styleable.SuperEditText_lineColor_unclick, lineColorunClick_default);
color = lineColor_unclick;
mPaint.setColor(lineColor_unclick); // 分割線默認顏色 = 灰色
setTextColor(color); // 字體默認顏色 = 灰色
// 3. 分割線位置
linePosition = typedArray.getInteger(R.styleable.SuperEditText_linePosition, 5);
// 消除自帶下劃線
setBackground(null);
/**
* 步驟3:經過監聽複寫EditText自己的方法來設置全部樣式
* 監聽方法:onTextChanged() & onFocusChanged()
* 調用時刻:當輸入框內容變化時 & 焦點發生變化時
*/
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
setDeleteIconVisible(hasFocus() && text.length() > 0,hasFocus());
// hasFocus()返回是否得到EditTEXT的焦點,便是否選中
// setDeleteIconVisible() = 根據傳入的是否選中 & 是否有輸入來判斷是否顯示刪除圖標->>關注1
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
setDeleteIconVisible(focused && length() > 0,focused);
// focused = 是否得到焦點
// 一樣根據setDeleteIconVisible()判斷是否要顯示刪除圖標->>關注1
}
/**
* 關注1
* 做用:設置分割線顏色
*/
private void setDeleteIconVisible(boolean deleteVisible,boolean leftVisible) {
color = leftVisible ? lineColor_click : lineColor_unclick;
setTextColor(color);
invalidate();
}
/**
* 步驟4:繪製分割線
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(color);
setTextColor(color);
// 繪製分割線
// 須要考慮:當輸入長度超過輸入框時,所畫的線須要跟隨着延伸
// 解決方案:線的長度 = 控件長度 + 延伸後的長度
int x=this.getScrollX(); // 獲取延伸後的長度
int w=this.getMeasuredWidth(); // 獲取控件長度
// 傳入參數時,線的長度 = 控件長度 + 延伸後的長度
canvas.drawLine(0, this.getMeasuredHeight()- linePosition, w+x,
this.getMeasuredHeight() - linePosition, mPaint);
}
}
複製代碼
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SuperEditText">
<attr name="ic_delete" format="reference" />
<attr name="delete_x" format="integer" />
<attr name="delete_y" format="integer" />
<attr name="delete_width" format="integer" />
<attr name="delete_height" format="integer" />
<attr name="ic_left_click" format="reference" />
<attr name="ic_left_unclick" format="reference" />
<attr name="left_x" format="integer" />
<attr name="left_y" format="integer" />
<attr name="left_width" format="integer" />
<attr name="left_height" format="integer" />
<attr name="lineColor_click" format="color" />
<attr name="lineColor_unclick" format="color" />
<attr name="linePosition" format="integer" />
<attr name="cursor" format="reference" />
</declare-styleable>
</resources>
複製代碼
cursor.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@color/lineColor_click" />
<size android:width="1dp" />
</shape>
複製代碼
Carson_Ho的Github地址:Super_EditText
具體請看文章:Android自定義View:你須要一款簡單實用的SuperEditText(一鍵刪除&自定義樣式)
SuperEditText
控件,具體請看:貢獻代碼說明Star
!SuperEditText
控件已在
Github
上開源:Super_EditText,歡迎Star
!
View
實例講解,有興趣能夠繼續關注Carson_Ho的安卓開發筆記