Android自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText
来源:互联网 发布:前瞻网数据库免费账号 编辑:程序博客网 时间:2024/05/17 20:24
前言
Android
开发中,EditText的使用 非常常见- 本文将手把手教你做一款 附带一键删除功能 & 自定义样式丰富的
SuperEditText
控件,希望你们会喜欢。
已在
Github
开源:Super_EditText,欢迎Star
!
目录
1. 简介
一款 附带一键删除功能 & 自定义样式丰富的 SuperEditText
控件
已在
Github
开源:Super_EditText,欢迎Star
!
2. 功能介绍
2.1 需求场景
对于 EditText
来说,一般的需求有:
- 方便用户因出现输入错误而进行2次输入
- 标识用户正在填写项
- 根据具体场景增加一定的
UI
元素
2.2 功能需求
根据需求场景,得出EditText
需要具备的功能如下:
- 一键删除
- 丰富的自定义样式:左侧图标、删除功能图标、分割线 & 光标 样式变化。具体如下图:
注:该样式的设置是系统自带的 API
所不具备的
- 功能列表
2.3 功能示意
3. 特点
对比市面上EditText
控件,该控件Super_EditText 的特点是:
3.1 功能实用
- 一键删除功能 在需求中非常常见,现将其封装后更加方便使用
- 可自定义样式程度高(比自带的强大 & 方便),不复杂却能满足一般的
EditText
使用需求可自定义样式如下:(注:该样式的设置是系统自带的
API
所不具备的)
3.2 使用简单
- 仅需要简单的
xml
属性配置 - 具体请看文章:Android自定义View:你需要一款简单实用的SuperEditText(一键删除&自定义样式)
3.3 二次开发成本低
- 本项目已在
Github
上开源:Super_EditText - 具备详细的源码分析文档(即本文)
所以,在其上做二次开发 & 定制化成本非常低。
4. 功能详细设计
下面将给出详细的功能逻辑
4.1 一键清空输入字段
- 描述:将当前用户输入的字段清空
- 需求场景:方便用户因出现输入错误而进行2次输入
- 原型图
- 源码分析
/* * 步骤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); }
4.2 选中样式
- 描述:通过增加UI元素 & 交互样式表示用户正在填写的项目
- 需求场景:标识用户正在填写项
- 样式说明
- 原型图
- 属性说明
- 源码分析
/* * 步骤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. 设置分割线颜色(使用十六进制代码,如#333、#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>
5. 完整源码地址
Carson_Ho的Github地址:Super_EditText
6. 具体使用
具体请看文章:Android自定义View:你需要一款简单实用的SuperEditText(一键删除&自定义样式)
7. 贡献代码
- 希望你们能和我一起完善这款简单 & 好用的
SuperEditText
控件,具体请看:贡献代码说明 - 关于该开源项目的意见 & 建议可在Issue上提出。欢迎
Star
!
8. 总结
- 相信你一定会喜欢上 这款简单 & 好用的
SuperEditText
控件已在
Github
上开源:Super_EditText,欢迎Star
!
- 下一篇文章我将继续进行一些 简单 & 实用的自定义
View
实例讲解,有兴趣可以继续关注Carson_Ho的安卓开发笔记
请帮顶 或 评论点赞!因为你的鼓励是我写作的最大动力!
阅读全文
6 3
- Android自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText
- Android自定义EditText:你需要一款简单实用的SuperEditText(一键删除&自定义样式)
- android EditText自定义样式
- Android EditText样式自定义
- android EditText自定义样式
- android EditText自定义样式
- Android自定义EditText样式
- android EditText自定义样式
- android EditText自定义样式
- Android自定义控件——手把手教你写出Google样式的ProgressBar
- 自定义的删除edittext
- Android Studio 自定义EditText样式.
- Android-手把手教你自定义view
- Android-EditText(自定义带删除功能的EditText)
- Android-EditText 自定义带删除功能的EditText
- Android-EditText(自定义带删除功能的EditText)
- android 自定义EditText 整合删除
- 手把手教你做一个自定义表格标签
- 使用mybatis-generator插件后的maven打包问题
- 自动回收资源的单例模式
- 解决Linux下分辨率低的问题
- AngularJs学习之路(二)
- Linux系统中两种安装go环境的方法
- Android自定义EditText:手把手教你做一款含一键删除&自定义样式的SuperEditText
- mysql数据库被黑客删除,您做好防范了吗?
- HDU-4417:Super Mario(树状数组+离线操作)
- HDU 1003 Max Sum-动态规划
- Android SmsManager(短信管理器),发送短信息
- USACO 2015 Dec Platinum 1.Max Flow题解,
- 文章标题
- 【Codeforces 837C. Two Seals】
- TCP/IP与UDP的区别