Android自定义View示例(一)—带有删除按钮的EditText

简介: MainActivity如下: package cc.textview5;import android.os.Bundle;import android.


MainActivity如下:

package cc.textview5;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import android.app.Activity;
/**
 * Demo描述:
 * 自定义控件实现带清除功能的EditText
 * 
 * 学习资料:
 * http://blog.csdn.net/xiaanming/article/details/11066685
 * 
 * Thank you very much
 */
public class MainActivity extends Activity {
    private CleanableEditText mUserNameCleanableEditText;
    private CleanableEditText mPassWordCleanableEditText;
    private Button mLoginButton;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		init();
	}
	
	private void init(){
		mUserNameCleanableEditText=(CleanableEditText) findViewById(R.id.userNameEditText);
		mPassWordCleanableEditText=(CleanableEditText) findViewById(R.id.passwordEditText);
		mLoginButton=(Button) findViewById(R.id.loginButton);
		mLoginButton.setOnClickListener(new OnClickListenerImpl());
	}
    
	private class OnClickListenerImpl implements OnClickListener {
		@Override
		public void onClick(View view) {
			if (TextUtils.isEmpty(mUserNameCleanableEditText.getText())) {
				mUserNameCleanableEditText.setShakeAnimation();
				Toast.makeText(MainActivity.this, "请输入用户名", Toast.LENGTH_SHORT).show();
			}

			if (TextUtils.isEmpty(mPassWordCleanableEditText.getText())) {
				mPassWordCleanableEditText.setShakeAnimation();
				Toast.makeText(MainActivity.this, "请输入密码", Toast.LENGTH_SHORT).show();
			}
		}

	}
	

}

CleanableEditText如下:

package cc.textview5;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.CycleInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.EditText;
/**
 * 在焦点变化时和输入内容发生变化时均要判断是否显示右边clean图标
 */
public class CleanableEditText extends EditText {
    private Drawable mRightDrawable;
    private boolean isHasFocus;
	
	public CleanableEditText(Context context) {
		super(context);
		init();
	}
	public CleanableEditText(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public CleanableEditText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}
	
	private void init(){
		//getCompoundDrawables:
		//Returns drawables for the left, top, right, and bottom borders.
		Drawable [] drawables=this.getCompoundDrawables();
		
		//取得right位置的Drawable
		//即我们在布局文件中设置的android:drawableRight
        mRightDrawable=drawables[2];	
	   
        //设置焦点变化的监听
        this.setOnFocusChangeListener(new FocusChangeListenerImpl());
        //设置EditText文字变化的监听
        this.addTextChangedListener(new TextWatcherImpl());
        //初始化时让右边clean图标不可见
        setClearDrawableVisible(false);
	}
	
	
	/**
	 * 当手指抬起的位置在clean的图标的区域
	 * 我们将此视为进行清除操作
	 * getWidth():得到控件的宽度
	 * event.getX():抬起时的坐标(改坐标是相对于控件本身而言的)
	 * getTotalPaddingRight():clean的图标左边缘至控件右边缘的距离
	 * getPaddingRight():clean的图标右边缘至控件右边缘的距离
	 * 于是:
	 * getWidth() - getTotalPaddingRight()表示:
	 * 控件左边到clean的图标左边缘的区域
	 * getWidth() - getPaddingRight()表示:
	 * 控件左边到clean的图标右边缘的区域
	 * 所以这两者之间的区域刚好是clean的图标的区域
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_UP:
			
			boolean isClean =(event.getX() > (getWidth() - getTotalPaddingRight()))&&
					         (event.getX() < (getWidth() - getPaddingRight()));
			if (isClean) {
				setText("");
			}
			break;

		default:
			break;
		}
		return super.onTouchEvent(event);
	}
	
	private class FocusChangeListenerImpl implements OnFocusChangeListener{
		@Override
		public void onFocusChange(View v, boolean hasFocus) {
             isHasFocus=hasFocus;
             if (isHasFocus) {
            	 boolean isVisible=getText().toString().length()>=1;
            	 setClearDrawableVisible(isVisible);
			} else {
                 setClearDrawableVisible(false);
			}
		}
		
	}
	
	//当输入结束后判断是否显示右边clean的图标
    private class TextWatcherImpl implements TextWatcher{
		@Override
		public void afterTextChanged(Editable s) {
			 boolean isVisible=getText().toString().length()>=1;
        	 setClearDrawableVisible(isVisible);
		}

		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,int after) {
			
		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before,int count) {
			
		}
    	
    }	
    
    //隐藏或者显示右边clean的图标
	protected void setClearDrawableVisible(boolean isVisible) {
		Drawable rightDrawable;
		if (isVisible) {
			rightDrawable = mRightDrawable;
		} else {
			rightDrawable = null;
		}
		//使用代码设置该控件left, top, right, and bottom处的图标
		setCompoundDrawables(getCompoundDrawables()[0],getCompoundDrawables()[1], 
				             rightDrawable,getCompoundDrawables()[3]);
	} 

	// 显示一个动画,以提示用户输入
	public void setShakeAnimation() {
		this.setAnimation(shakeAnimation(5));
	}

	//CycleTimes动画重复的次数
	public Animation shakeAnimation(int CycleTimes) {
		Animation translateAnimation = new TranslateAnimation(0, 10, 0, 10);
		translateAnimation.setInterpolator(new CycleInterpolator(CycleTimes));
		translateAnimation.setDuration(1000);
		return translateAnimation;
	}

}

main.xml如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="20dip"
    android:paddingRight="20dip"
    >

    <cc.textview5.CleanableEditText
        android:id="@+id/userNameEditText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="username"
        android:layout_marginTop="30dip"
        android:drawableLeft="@drawable/icon_user"
        android:drawableRight="@drawable/clean_selector"
         />
    
     <cc.textview5.CleanableEditText
        android:id="@+id/passwordEditText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="password"
        android:password="true"
        android:layout_marginTop="100dip"
        android:drawableLeft="@drawable/account_icon"
        android:drawableRight="@drawable/clean_selector"
         />
     
     <Button
         android:id="@+id/loginButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Login"
        android:layout_marginTop="155dip"
         />

</RelativeLayout>



相关文章
|
3天前
|
API Android开发 开发者
Android经典实战之使用ViewCompat来处理View兼容性问题
本文介绍Android中的`ViewCompat`工具类,它是AndroidX库核心部分的重要兼容性组件,确保在不同Android版本间处理视图的一致性。文章列举了设置透明度、旋转、缩放、平移等功能,并提供了背景色、动画及用户交互等实用示例。通过`ViewCompat`,开发者可轻松实现跨版本视图操作,增强应用兼容性。
20 5
|
18天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
39 2
|
1月前
|
Android开发
Android面试题之自定义View注意事项
在Android开发中,自定义View主要分为四类:直接继承View重写onDraw,继承ViewGroup创建布局,扩展特定View如TextView,以及继承特定ViewGroup。实现时需注意:支持wrap_content通过onMeasure处理,支持padding需在onDraw或onMeasure/onLayout中处理。避免在View中使用Handler,使用post系列方法代替。记得在onDetachedFromWindow时停止线程和动画以防止内存泄漏。处理滑动嵌套时解决滑动冲突,并避免在onDraw中大量创建临时对象。
23 4
|
1月前
|
Android开发
Android面试题之View的invalidate方法和postInvalidate方法有什么区别
本文探讨了Android自定义View中`invalidate()`和`postInvalidate()`的区别。`invalidate()`在UI线程中刷新View,而`postInvalidate()`用于非UI线程,通过消息机制切换到UI线程执行`invalidate()`。源码分析显示,`postInvalidate()`最终调用`ViewRootImpl`的`dispatchInvalidateDelayed`,通过Handler发送消息到UI线程执行刷新。
27 1
|
19天前
|
机器学习/深度学习 人工智能 算法
探索AI在医疗影像分析中的应用探索安卓开发中的自定义View组件
【7月更文挑战第31天】随着人工智能技术的飞速发展,其在医疗健康领域的应用日益广泛。本文将聚焦于AI技术在医疗影像分析中的运用,探讨其如何通过深度学习模型提高诊断的准确性和效率。我们将介绍一些关键的深度学习算法,并通过实际代码示例展示这些算法是如何应用于医学影像的处理和分析中。文章旨在为读者提供对AI在医疗领域应用的深刻理解和实用知识。
22 0
|
26天前
|
消息中间件 调度 Android开发
Android经典面试题之View的post方法和Handler的post方法有什么区别?
本文对比了Android开发中`View.post`与`Handler.post`的使用。`View.post`将任务加入视图关联的消息队列,在视图布局后执行,适合视图操作。`Handler.post`更通用,可调度至特定Handler的线程,不仅限于视图任务。选择方法取决于具体需求和上下文。
26 0
|
1月前
|
消息中间件 前端开发 Android开发
Android面试题自定义View之Window、ViewRootImpl和View的三大流程
Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
24 0