Hello啊各位老铁,今天还是一篇关于自定义View相关的,带来一个大众的,常见的一个输入框,很多的场合下都能遇到,比如验证码,密码框等等,配置了很多常见的属性,可以满足不同场合下的需求,矩形框,圆角框,下划线等等均可满足,长度设置,光标选择,背景选择,均可控制,废话不多数,我们直接进入正题。
今天的内容大致如下:
1、效果及代码具体调用。
2、具体实现过程。
3、开源地址。
4、总结及注意事项。
一、效果及代码具体调用。
效果展示
边框黑圆圈展示
边框文字展示
纯色背景文字展示
纯色背景黑圆圈展示
纯色背景星号展示
下划线文字展示
下划线黑圆圈展示
纯色背景横向光标展示
能实现的效果还有很多,大家可以根据属性来动态配置即可。
关于使用方式,大家可以下载源码,直接复制即可,毕竟只有一个类,如果懒得下载源码,使用我给大家准备的远程Maven也是可以的,也是非常的方便,远程Maven具体使用如下。
Maven具体调用
1、在你的根项目下的build.gradle文件下,引入maven。
allprojects { repositories { maven { url"https://gitee.com/AbnerAndroid/almighty/raw/master" } } }
2、在你需要使用的Module中build.gradle文件下,引入依赖。
dependencies { implementation'com.vip:edit:1.0.3'}
代码使用
<com.vip.edit.InputBoxViewandroid:id="@+id/ib_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginTop="30dp"android:layout_marginRight="10dp"app:input_background="#f5f5f5"app:input_canvas_type="rect"app:input_length="6"app:input_text_size="16sp"app:input_text_type="round"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"/>
属性介绍
属性 |
类型 |
概述 |
input_canvas_type |
enum |
绘制类型,目前有三种,线(line),矩形(rect),圆角(round),圆角需要结合属性input_radius使用 |
input_canvas_style |
enum |
绘制画笔类型,空心还是实心,空心(stroke),实心(fill),实心和空心(fill_and_stroke) |
input_background |
color |
输入框的背景 |
input_select_background |
color |
输入框的选择背景 |
input_radius |
dimension |
输入框圆角度数 |
input_line_height |
dimension |
输入框下划线的高度 |
input_length |
integer |
输入框的长度 |
input_spacing |
dimension |
输入框的间距 |
input_text_color |
color |
输入框的内容颜色 |
input_text_size |
dimension |
输入框的文字大小 |
input_text_type |
enum |
输入框的文字类型,普通文字(text),星号(asterisk),黑圆圈(round) |
input_is_cursor |
boolean |
输入框是否有光标,默认展示光标 |
input_cursor_direction |
boolean |
输入框光标方向 |
input_cursor_width |
dimension |
输入框光标宽度 |
input_cursor_color |
color |
输入框光标颜色 |
input_cursor_spacing |
color |
输入框光标间距 |
input_cursor_is_twinkle |
boolean |
输入框的光标是否闪烁 |
input_is_android_keyboard |
boolean |
输入框是否弹起原生的软件盘,默认谈起,可以调用自定义的键盘 |
input_cursor_margin_bottom |
dimension |
横向的光标距离底部的距离 |
方法介绍
方法 |
参数 |
概述 |
clearContent |
无参 |
清空内容 |
setContent |
String |
设置内容 |
hideInputMethod |
无参 |
隐藏软键盘,使用系统软键盘时 |
showKeyBoard |
回调函数 |
需要弹起自己的软键盘时可以调用 |
inputChangeContent |
回调函数 |
获取连续的输入结果 |
inputEndResult |
回调函数 |
获取最终的输入内容,当等于你设置的length时进行回调 |
二、具体实现过程。
实现的过程也是非常的简单,大致可以分为五步走,1、绘制方格或下划线,2、绘制内容,3、绘制光标,4、实现光标闪动,5、软键盘控制,来,我们一步一步的来实现。
1、绘制方格或下划线
绘制方格或下划线,如下草图所示,需要根据传递的数量来进行绘制,首先要计算出每一格的宽度,也就是屏幕的宽度-格子之间的边距/格子的数量。
//每个输入框的宽度=屏幕的宽-左右的边距-输入框直接的间距/输入框的个数mRectWidth= (width-mSpacing* (mLength-1)) /mLength
得到了每一格的宽度之后,就可以根据数量来进行动态的绘制了,无非就是遍历,根据属性input_canvas_type来绘制不同的效果,mSelectBackGroundColor变量为属性input_select_background设置的值,用来标记选择的输入框颜色,如下图所示:
如果,你想要改变选中的格子边框颜色,就可以进行设置颜色值,同样的需要搭配画笔的绘制样式,如下代码所示:
/*** AUTHOR:AbnerMing* INTRODUCE:绘制输入框*/privatefuncanvasInputBox(canvas: Canvas?) { mPaint!!.apply { color=mBackGroundColor//设置背景颜色strokeCap=Paint.Cap.ROUND//圆角线 } for (ain0untilmLength) { valtextLength=text.toString().length//当前输入的长度if (mSelectBackGroundColor!=0) { varpaintStyle=Paint.Style.STROKEwhen (mInputCanvasStyle) { 0-> { paintStyle=Paint.Style.STROKE } 1-> { paintStyle=Paint.Style.FILL } 2-> { paintStyle=Paint.Style.FILL_AND_STROKE } } if (a==textLength) { mPaint!!.apply { style=paintStylecolor=mSelectBackGroundColor//设置选中背景颜色 } } else { mPaint!!.apply { style=paintStylecolor=mBackGroundColor//设置背景颜色 } } } valleft=a*mRectWidth+a*mSpacingvaltop=0fvalright= (a+1) *mRectWidth+a*mSpacingvalbottom=height.toFloat() when (mInputCanvasType) { 0-> { //绘制下划线canvas?.drawRoundRect( left, bottom-mLineHeight, right, bottom, mRadius, mRadius, mPaint!! ) } 1-> { //绘制矩形canvas?.drawRect(left, top, right, bottom, mPaint!!) } 2-> { //绘制圆角矩形canvas?.drawRoundRect(left, top, right, bottom, mRadius, mRadius, mPaint!!) } } } }
绘制格子,最重要的就是计算每个格子的位置,其实只需要考虑X的坐标即可,Y可以直接充满View的高度。
2、绘制内容
绘制输入的内容,和绘制格子一样,重要的就是计算位置,有了格子的位置之后,计算内容就比较的简单了,只需要获取格子的中间坐标即可,计算如下:首先,拿到每个格子的右边X坐标点,再减去格子宽度的一半,就得到的中间的X坐标,但是,文字的绘制,还需要减去文字宽度的一半,这个一定要注意,否则,文字就是从中间点往右进行绘制了,就偏移了中间点。
文字的X轴计算如下:
valtextX= ((a+1) *mRectWidth) +a*mSpacing-mRectWidth/2-w/2
同理,Y的计算方式类似,全部代码如下,有一点需要注意下,就是星号,星号和文字以及圆圈还是有不一样的地方,那就比较小,那么就需要特殊的处理一下,都是基础的代码,没什么好说的。
/*** AUTHOR:AbnerMing* INTRODUCE:绘制内容*/privatefundrawText(canvas: Canvas?) { mPaint!!.apply { style=Paint.Style.FILLcolor=mTextColor//设置内容颜色textSize=mTextSize } if (!TextUtils.isEmpty(text)) { for (aintext!!.indices) { valcontent=text!![a].toString() varendContent=contentif (mTextType==1) { endContent="*" } elseif (mTextType==2) { endContent="●" } valrect=Rect() mPaint!!.getTextBounds(endContent, 0, content.length, rect) valw=mPaint!!.measureText(endContent)//获取文字的宽//获取文字的X坐标valtextX= ((a+1) *mRectWidth) +a*mSpacing-mRectWidth/2-w/2valh=rect.height() //获取文字的Y坐标vartextY= (height+h) /2.0f//针对星号做特殊处理if (mTextType==1) { textY+=mTextSize/3 } canvas?.drawText(endContent, textX, textY, mPaint!!) } } }
3、绘制光标
绘制光标就比较简单了,无非就是纵向还是横向,也是根据设置的属性来控制的,纵向计算出X坐标即可,横向就计算出Y的坐标即可。需要注意的是,距离左右或者上下的间距控制,代码如下:
/*** AUTHOR:AbnerMing* INTRODUCE:绘制光标*/privatefundrawCursor(canvas: Canvas?) { mCursorPaint!!.apply { strokeWidth=mCursorWidthisAntiAlias=true } //需要根据当前输入的位置,计算光标的绘制位置vallen=text?.lengthif (len!!<mLength) { if (mCursorDirection) { //纵向光标valrectWidth= ((len+1) *mRectWidth) +len*mSpacing-mRectWidth/2canvas?.drawLine( rectWidth, mCursorSpacing, rectWidth, height-mCursorSpacing, mCursorPaint!! ) } else { valendX= ((len+1) *mRectWidth) +len*mSpacingvalstartX=endX-mRectWidth//横向光标canvas?.drawLine( startX+mCursorSpacing, height.toFloat() -mCursorMarginBottom,//减去距离底部的边距endX-mCursorSpacing, height.toFloat() -mCursorMarginBottom, mCursorPaint!! ) } } }
4、实现光标闪动
光标闪动,使用了一个属性动画,设置无限循环,然后控制画笔的颜色即可。
privatevalcursorAnim: ValueAnimator=ValueAnimator.ofInt(0, 2).apply { duration=1000repeatCount=ValueAnimator.INFINITE//无线循环repeatMode=ValueAnimator.RESTART//正序 }
在onAttachedToWindow方法里做启动动画操作:mCursorTwinkle为是否需要光标,需要再启动,是通过属性input_cursor_is_twinkle来控制的。
if (mCursorTwinkle) { //不在运行,开启动画if (!cursorAnim.isRunning) { cursorAnim.start() } cursorAnim.addUpdateListener { valv=it.animatedValueasIntif (v==0) { mCursorPaint?.color=Color.TRANSPARENT } else { mCursorPaint?.color=mCursorColor } postInvalidate() } }
同样的,当onDetachedFromWindow方法时,就需要结束。
if (mCursorTwinkle) { if (cursorAnim.isRunning||cursorAnim.isStarted) { cursorAnim.end() } cursorAnim.removeAllUpdateListeners() }
判断在文字的输入时进行闪烁,这个是很重要的,也就是闪烁的位置,一定是当前的输入位置,未输入就是第一格闪烁,依次类推,输入完成,就结束闪烁。
overridefunonTextChanged( text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int ) { super.onTextChanged(text, start, lengthBefore, lengthAfter) if (!mIsAttachedToWindows) return//输入框的光标是否闪烁if (mCursorTwinkle) { if ((text?.length?: 0) >=mLength) { cursorAnim.takeIf { it.isStarted||it.isRunning }?.end() } elseif (!cursorAnim.isRunning) { cursorAnim.start() } } valendContent=text.toString() if (endContent.length==mLength) { //一样的话,进行回调mEndContentResult?.invoke(endContent) } mChangeContent?.invoke(endContent) }
5、软键盘控制
软件盘控制,有两种方式,一种是弹出系统的软键盘,一种是弹出自定义的软键盘,这个控制也是由传递的属性input_is_android_keyboard来操作的,默认为true,弹出系统的,否则就弹出自定义的,针对自定义的弹出,需要暴露出实现的方法,由使用者进行实现。
/*** AUTHOR:AbnerMing* INTRODUCE:弹起软件盘*/privatefunshowKeyboard() { if (mIsAndroidKeyBoard) { isFocusable=trueisFocusableInTouchMode=truerequestFocus() valim=context.getSystemService(Context.INPUT_METHOD_SERVICE) as?InputMethodManager?: returnim.showSoftInput(this, InputMethodManager.SHOW_FORCED) } else { //启用自定义的软键盘if (mKeyBoard!=null) { mKeyBoard?.invoke() } } }
mKeyBoard为弹出自定义软键盘回调函数,代码如下:
/*** AUTHOR:AbnerMing* INTRODUCE:显示自己定义的软件盘*/privatevarmKeyBoard: (() ->Unit?)?=nullfunshowKeyBoard(block: () ->Unit) { mKeyBoard=block }
隐藏软键盘操作,可以在页面隐藏时进行触发,目前在自定义View中onDetachedFromWindow方法里进行了调用,当然,你可以自己选择性调用。
/*** AUTHOR:AbnerMing* INTRODUCE:隐藏软件盘*/funhideInputMethod() { if (mIsAndroidKeyBoard) { valimm: InputMethodManager=context.getSystemService(Context.INPUT_METHOD_SERVICE) asInputMethodManagerimm.hideSoftInputFromWindow(windowToken, 0) //强制隐藏 } }
三、开源地址。
目前项目已经开源,需要的朋友可以查看:https://github.com/AbnerMing888/InputBoxView
四、总结及注意事项。
1、触摸输入框,默认是弹出系统自带的软键盘的,如果想使用自定义的,设置属性input_is_android_keyboard为false即可,并调用showKeyBoard回调函数,在showKeyBoard方法里进行书写弹起自定义软键盘即可。
2、如果绘制类型属性input_canvas_type为round,也就是圆角时,需要结合input_radius这个属性,来实现圆角的角度大小。
3、光标的方向属性input_cursor_direction是一个boolean类型的值,默认是纵向的,false是为横向的。
4、当输入框的选择背景input_select_background不为空时,画笔属性input_canvas_style才会生效。