編輯:關於Android編程
曾經做過一個項目,其中登錄界面的交互令人印象深刻。交互設計師給出了一個非常作的設計,要求做出包含根據情況可變色的下劃線,左側有可變圖標,右側有可變刪除標志的輸入框,如圖
記錄制作過程:
第一版本
public class LineEditText extends EditText { private Paint mPaint; private int color; public static final int STATUS_FOCUSED = 1; public static final int STATUS_UNFOCUSED = 2; public static final int STATUS_ERROR = 3; private int status = 2; private Drawable del_btn; private Drawable del_btn_down; private int focusedDrawableId = R.drawable.user_select;// 默認的 private int unfocusedDrawableId = R.drawable.user; private int errorDrawableId = R.drawable.user_error; Drawable left = null; private Context mContext; public LineEditText(Context context) { super(context); mContext = context; init(); } public LineEditText(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } public LineEditText(Context context, AttributeSet attrs, int defStryle) { super(context, attrs, defStryle); mContext = context; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lineEdittext, defStryle, 0); focusedDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableFocus, R.drawable.user_select); unfocusedDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableUnFocus, R.drawable.user); errorDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableError, R.drawable.user_error); a.recycle(); init(); } /** * 2014/7/31 * * @author Aimee.ZHANG */ private void init() { mPaint = new Paint(); // mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(3.0f); color = Color.parseColor("#bfbfbf"); setStatus(status); del_btn = mContext.getResources().getDrawable(R.drawable.del_but_bg); del_btn_down = mContext.getResources().getDrawable(R.drawable.del_but_bg_down); addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void afterTextChanged(Editable arg0) { setDrawable(); } }); setDrawable(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(color); canvas.drawLine(0, this.getHeight() - 1, this.getWidth(), this.getHeight() - 1, mPaint); } // 刪除圖片 private void setDrawable() { if (length() < 1) { setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn, null); } else { setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn_down,null); } } // 處理刪除事件 @Override public boolean onTouchEvent(MotionEvent event) { if (del_btn_down != null && event.getAction() == MotionEvent.ACTION_UP) { int eventX = (int) event.getRawX(); int eventY = (int) event.getRawY(); Log.e("eventXY", "eventX = " + eventX + "; eventY = " + eventY); Rect rect = new Rect(); getGlobalVisibleRect(rect); rect.left = rect.right - 50; if (rect.contains(eventX, eventY)) setText(""); } return super.onTouchEvent(event); } public void setStatus(int status) { this.status = status; if (status == STATUS_ERROR) { try { left = getResources().getDrawable(errorDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#f57272")); } else if (status == STATUS_FOCUSED) { try { left = getResources().getDrawable(focusedDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#5e99f3")); } else { try { left = getResources().getDrawable(unfocusedDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#bfbfbf")); } if (left != null) { // left.setBounds(0, 0, 30, 40); // this.setCompoundDrawables(left, null, null, null); setCompoundDrawablesWithIntrinsicBounds(left,null,del_btn,null); } postInvalidate(); } public void setLeftDrawable(int focusedDrawableId, int unfocusedDrawableId, int errorDrawableId) { this.focusedDrawableId = focusedDrawableId; this.unfocusedDrawableId = unfocusedDrawableId; this.errorDrawableId = errorDrawableId; setStatus(status); } @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); if (focused) { setStatus(STATUS_FOCUSED); } else { setStatus(STATUS_UNFOCUSED); } } @Override protected void finalize() throws Throwable { super.finalize(); }; public void setColor(int color) { this.color = color; this.setTextColor(color); invalidate(); } }
效果圖:
代碼解釋:
變量名 STATUS_FOCUSED,STATUS_UNFOCUSED,STATUS_ERROR 標示了三種狀態,選中狀況為藍色,未選中狀態為灰色,錯誤狀態為紅色。 focusedDrawableId unfocusedDrawableId errorDrawableId 存放三種狀態的圖片,放置於最左側。
canvas.drawLine(0, this.getHeight() - 1, this.getWidth(),this.getHeight() - 1, mPaint); //畫editText 最下方的線 setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn, null); //放置左邊的和右邊的圖片(左,上,右,下) 相當於 android:drawableLeft="" android:drawableRight=""
1、onTouchEvent 當手機點擊時,第一個先執行的函數,當點擊右側刪除圖標是清空 edittext
2、setStatus 根據不同的狀態,左邊的圖片不一樣
存在的問題: 這版本雖然基本功能已經實現,但是不符合需求,設計中要求文本框中無文字時,右側刪除按鈕不顯示,不點擊刪除按鈕,刪除按鈕要保持灰色,點擊時才可以變藍色。
因此有了第二個版本
public class LineEditText extends EditText implements TextWatcher, <br /> OnFocusChangeListener{ private Paint mPaint; private int color; public static final int STATUS_FOCUSED = 1; public static final int STATUS_UNFOCUSED = 2; public static final int STATUS_ERROR = 3; private int status = 2; private Drawable del_btn; private Drawable del_btn_down; private int focusedDrawableId = R.drawable.user_select;// 默認的 private int unfocusedDrawableId = R.drawable.user; private int errorDrawableId = R.drawable.user_error; Drawable left = null; private Context mContext; /** * 是否獲取焦點,默認沒有焦點 */ private boolean hasFocus = false; /** * 手指抬起時的X坐標 */ private int xUp = 0; public LineEditText(Context context) { super(context); mContext = context; init(); } public LineEditText(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; init(); } public LineEditText(Context context, AttributeSet attrs, int defStryle) { super(context, attrs, defStryle); mContext = context; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lineEdittext, defStryle, 0); focusedDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableFocus, R.drawable.user_select); unfocusedDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableUnFocus, R.drawable.user); errorDrawableId = a.getResourceId( R.styleable.lineEdittext_drawableError, R.drawable.user_error); a.recycle(); init(); } /** * 2014/7/31 * * @author Aimee.ZHANG */ private void init() { mPaint = new Paint(); // mPaint.setStyle(Paint.Style.FILL); mPaint.setStrokeWidth(3.0f); color = Color.parseColor("#bfbfbf"); setStatus(status); del_btn = mContext.getResources().getDrawable(R.drawable.del_but_bg); del_btn_down = mContext.getResources().getDrawable(R.drawable.del_but_bg_down); addListeners(); setCompoundDrawablesWithIntrinsicBounds(left, null, null, null); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(color); canvas.drawLine(0, this.getHeight() - 1, this.getWidth(), this.getHeight() - 1, mPaint); } // 刪除圖片 // private void setDrawable() { // if (length() < 1) { // setCompoundDrawablesWithIntrinsicBounds(left, null, null, null); // } else { // setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn,null); // } // } // 處理刪除事件 @Override public boolean onTouchEvent(MotionEvent event) { if (del_btn != null && event.getAction() == MotionEvent.ACTION_UP) { // 獲取點擊時手指抬起的X坐標 xUp = (int) event.getX(); Log.e("xUp", xUp+""); /*Rect rect = new Rect(); getGlobalVisibleRect(rect); rect.left = rect.right - 50;*/ // 當點擊的坐標到當前輸入框右側的距離小於等於 getCompoundPaddingRight() 的距離時,則認為是點擊了刪除圖標 if ((getWidth() - xUp) <= getCompoundPaddingRight()) { if (!TextUtils.isEmpty(getText().toString())) { setText(""); } } }else if(del_btn != null && event.getAction() == MotionEvent.ACTION_DOWN && getText().length()!=0){ setCompoundDrawablesWithIntrinsicBounds(left,null,del_btn_down,null); }else if(getText().length()!=0){ setCompoundDrawablesWithIntrinsicBounds(left,null,del_btn,null); } return super.onTouchEvent(event); } public void setStatus(int status) { this.status = status; if (status == STATUS_ERROR) { try { left = getResources().getDrawable(errorDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#f57272")); } else if (status == STATUS_FOCUSED) { try { left = getResources().getDrawable(focusedDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#5e99f3")); } else { try { left = getResources().getDrawable(unfocusedDrawableId); } catch (NotFoundException e) { e.printStackTrace(); } setColor(Color.parseColor("#bfbfbf")); } if (left != null) { // left.setBounds(0, 0, 30, 40); // this.setCompoundDrawables(left, null, null, null); setCompoundDrawablesWithIntrinsicBounds(left,null,null,null); } postInvalidate(); } public void setLeftDrawable(int focusedDrawableId, int unfocusedDrawableId, int errorDrawableId) { this.focusedDrawableId = focusedDrawableId; this.unfocusedDrawableId = unfocusedDrawableId; this.errorDrawableId = errorDrawableId; setStatus(status); } private void addListeners() { try { setOnFocusChangeListener(this); addTextChangedListener(this); } catch (Exception e) { e.printStackTrace(); } } @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(focused, direction, previouslyFocusedRect); this.hasFocus=focused; if (focused) { setStatus(STATUS_FOCUSED); } else { setStatus(STATUS_UNFOCUSED); setCompoundDrawablesWithIntrinsicBounds(left,null,null,null); } } @Override protected void finalize() throws Throwable { super.finalize(); }; public void setColor(int color) { this.color = color; this.setTextColor(color); invalidate(); } @Override public void afterTextChanged(Editable arg0) { // TODO Auto-generated method stub postInvalidate(); } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { // TODO Auto-generated method stub if (TextUtils.isEmpty(arg0)) { // 如果為空,則不顯示刪除圖標 setCompoundDrawablesWithIntrinsicBounds(left, null, null, null); } else { // 如果非空,則要顯示刪除圖標 setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn, null); } } @Override public void onTextChanged(CharSequence s, int start, int before, int after) { if (hasFocus) { if (TextUtils.isEmpty(s)) { // 如果為空,則不顯示刪除圖標 setCompoundDrawablesWithIntrinsicBounds(left, null, null, null); } else { // 如果非空,則要顯示刪除圖標 setCompoundDrawablesWithIntrinsicBounds(left, null, del_btn, null); } } } @Override public void onFocusChange(View arg0, boolean arg1) { // TODO Auto-generated method stub try { this.hasFocus = arg1; } catch (Exception e) { e.printStackTrace(); } } }
比較關鍵的方法是:onTouchEvent
當進入界面,點擊輸入框,要判斷輸入框中是否已有文字,如果有則顯示灰色的刪除按鈕,如果沒有則不顯示,如果點擊了刪除按鈕,刪除按鈕變藍色
存在的問題: 這個版本依舊存在問題,就是輸入長度超過輸入框,所畫的線不會延伸,如圖
解決方法:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(color); int x=this.getScrollX(); int w=this.getMeasuredWidth(); canvas.drawLine(0, this.getHeight() - 1, w+x, this.getHeight() - 1, mPaint); }
w:獲取控件長度
X:延伸後的長度
最終效果:
以上就是Android實現自定義的EditText下劃線的方法,希望對大家的學習有所幫助。
之前曾經在網上看到Android仿QQ空間底部菜單的Demo,發現這個Demo有很多Bug,布局用了很多神秘數字。於是研究了一下QQ空間底部菜單的實現,自己寫了一個,供大
前言:為了使ListView性能更優,最普遍的方法就是添加一個ViewHolder靜態類。雖然性能有很大的提高,但是同樣也伴隨著Item控件內容顯示重復或錯亂的情況。&n
使用ListView顯示一個長的項列表 1、新建一個名為“BasicView5”的Android項目; 2、修改BasicView5.java文件,修改後的程序如下: p
京東淘寶有那麼一種效果就是,上拉可以查看寶貝的詳情,這裡我也實現了一個類似的效果,也可以移植到商業項目上:先看看簡單的效果圖實現原理其實是利用了ScrollView的滾動