注册

一个简单的自定义输入框

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.InputBoxView
android: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_typeenum绘制类型,目前有三种,线(line),矩形(rect),圆角(round),圆角需要结合属性input_radius使用
input_canvas_styleenum绘制画笔类型,空心还是实心,空心(stroke),实心(fill),实心和空心(fill_and_stroke)
input_backgroundcolor输入框的背景
input_select_backgroundcolor输入框的选择背景
input_radiusdimension输入框圆角度数
input_line_heightdimension输入框下划线的高度
input_lengthinteger输入框的长度
input_spacingdimension输入框的间距
input_text_colorcolor输入框的内容颜色
input_text_sizedimension输入框的文字大小
input_text_typeenum输入框的文字类型,普通文字(text),星号(asterisk),黑圆圈(round)
input_is_cursorboolean输入框是否有光标,默认展示光标
input_cursor_directionboolean输入框光标方向
input_cursor_widthdimension输入框光标宽度
input_cursor_colorcolor输入框光标颜色
input_cursor_spacingcolor输入框光标间距
input_cursor_is_twinkleboolean输入框的光标是否闪烁
input_is_android_keyboardboolean输入框是否弹起原生的软件盘,默认谈起,可以调用自定义的键盘
input_cursor_margin_bottomdimension横向的光标距离底部的距离

方法介绍










































方法参数概述
clearContent无参清空内容
setContentString设置内容
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:绘制输入框
*/
private fun canvasInputBox(canvas: Canvas?) {
mPaint!!.apply {
color = mBackGroundColor//设置背景颜色
strokeCap = Paint.Cap.ROUND//圆角线
}


for (a in 0 until mLength) {

val textLength = text.toString().length//当前输入的长度

if (mSelectBackGroundColor != 0) {
var paintStyle = Paint.Style.STROKE
when (mInputCanvasStyle) {
0 -> {
paintStyle = Paint.Style.STROKE
}
1 -> {
paintStyle = Paint.Style.FILL
}
2 -> {
paintStyle = Paint.Style.FILL_AND_STROKE
}
}
if (a == textLength) {
mPaint!!.apply {
style = paintStyle
color = mSelectBackGroundColor//设置选中背景颜色
}
} else {
mPaint!!.apply {
style = paintStyle
color = mBackGroundColor//设置背景颜色
}

}
}

val left = a * mRectWidth + a * mSpacing
val top = 0f
val right = (a + 1) * mRectWidth + a * mSpacing
val bottom = 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轴计算如下:


  val textX = ((a + 1) * mRectWidth) + a * mSpacing - mRectWidth / 2 - w / 2

同理,Y的计算方式类似,全部代码如下,有一点需要注意下,就是星号,星号和文字以及圆圈还是有不一样的地方,那就比较小,那么就需要特殊的处理一下,都是基础的代码,没什么好说的。


/**
* AUTHOR:AbnerMing
* INTRODUCE:绘制内容
*/
private fun drawText(canvas: Canvas?) {
mPaint!!.apply {
style = Paint.Style.FILL
color = mTextColor//设置内容颜色
textSize = mTextSize
}

if (!TextUtils.isEmpty(text)) {
for (a in text!!.indices) {
val content = text!![a].toString()
var endContent = content

if (mTextType == 1) {
endContent = "*"
} else if (mTextType == 2) {
endContent = "●"
}

val rect = Rect()
mPaint!!.getTextBounds(endContent, 0, content.length, rect)
val w = mPaint!!.measureText(endContent)//获取文字的宽
//获取文字的X坐标
val textX = ((a + 1) * mRectWidth) + a * mSpacing - mRectWidth / 2 - w / 2
val h = rect.height()
//获取文字的Y坐标
var textY = (height + h) / 2.0f
//针对星号做特殊处理
if (mTextType == 1) {
textY += mTextSize / 3
}

canvas?.drawText(endContent, textX, textY, mPaint!!)
}
}
}

3、绘制光标


绘制光标就比较简单了,无非就是纵向还是横向,也是根据设置的属性来控制的,纵向计算出X坐标即可,横向就计算出Y的坐标即可。需要注意的是,距离左右或者上下的间距控制,代码如下:


 /**
* AUTHOR:AbnerMing
* INTRODUCE:绘制光标
*/
private fun drawCursor(canvas: Canvas?) {
mCursorPaint!!.apply {
strokeWidth = mCursorWidth
isAntiAlias = true
}
//需要根据当前输入的位置,计算光标的绘制位置
val len = text?.length
if (len!! < mLength) {
if (mCursorDirection) {
//纵向光标
val rectWidth = ((len + 1) * mRectWidth) + len * mSpacing - mRectWidth / 2
canvas?.drawLine(
rectWidth,
mCursorSpacing,
rectWidth,
height - mCursorSpacing,
mCursorPaint!!
)
} else {
val endX = ((len + 1) * mRectWidth) + len * mSpacing
val startX = endX - mRectWidth
//横向光标
canvas?.drawLine(
startX + mCursorSpacing,
height.toFloat() - mCursorMarginBottom,//减去距离底部的边距
endX - mCursorSpacing,
height.toFloat() - mCursorMarginBottom,
mCursorPaint!!
)
}
}

}

4、实现光标闪动


光标闪动,使用了一个属性动画,设置无限循环,然后控制画笔的颜色即可。


    private val cursorAnim: ValueAnimator = ValueAnimator.ofInt(0, 2).apply {
duration = 1000
repeatCount = ValueAnimator.INFINITE//无线循环
repeatMode = ValueAnimator.RESTART//正序
}

在onAttachedToWindow方法里做启动动画操作:mCursorTwinkle为是否需要光标,需要再启动,是通过属性input_cursor_is_twinkle来控制的。


if (mCursorTwinkle) {
//不在运行,开启动画
if (!cursorAnim.isRunning) {
cursorAnim.start()
}
cursorAnim.addUpdateListener {
val v = it.animatedValue as Int
if (v == 0) {
mCursorPaint?.color = Color.TRANSPARENT
} else {
mCursorPaint?.color = mCursorColor
}
postInvalidate()
}
}

同样的,当onDetachedFromWindow方法时,就需要结束。


if (mCursorTwinkle) {
if (cursorAnim.isRunning || cursorAnim.isStarted) {
cursorAnim.end()
}
cursorAnim.removeAllUpdateListeners()
}

判断在文字的输入时进行闪烁,这个是很重要的,也就是闪烁的位置,一定是当前的输入位置,未输入就是第一格闪烁,依次类推,输入完成,就结束闪烁。


override fun onTextChanged(
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()
} else if (!cursorAnim.isRunning) {
cursorAnim.start()
}
}

val endContent = text.toString()
if (endContent.length == mLength) {
//一样的话,进行回调
mEndContentResult?.invoke(endContent)
}

mChangeContent?.invoke(endContent)

}

5、软键盘控制


软件盘控制,有两种方式,一种是弹出系统的软键盘,一种是弹出自定义的软键盘,这个控制也是由传递的属性input_is_android_keyboard来操作的,默认为true,弹出系统的,否则就弹出自定义的,针对自定义的弹出,需要暴露出实现的方法,由使用者进行实现。


 /**
* AUTHOR:AbnerMing
* INTRODUCE:弹起软件盘
*/
private fun showKeyboard() {
if (mIsAndroidKeyBoard) {
isFocusable = true
isFocusableInTouchMode = true
requestFocus()
val im =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
?: return
im.showSoftInput(this, InputMethodManager.SHOW_FORCED)
} else {
//启用自定义的软键盘
if (mKeyBoard != null) {
mKeyBoard?.invoke()
}
}
}

mKeyBoard为弹出自定义软键盘回调函数,代码如下:


    /**
* AUTHOR:AbnerMing
* INTRODUCE:显示自己定义的软件盘
*/
private var mKeyBoard: (() -> Unit?)? = null
fun showKeyBoard(block: () -> Unit) {
mKeyBoard = block
}

隐藏软键盘操作,可以在页面隐藏时进行触发,目前在自定义View中onDetachedFromWindow方法里进行了调用,当然,你可以自己选择性调用。


    /**
* AUTHOR:AbnerMing
* INTRODUCE:隐藏软件盘
*/
fun hideInputMethod() {
if (mIsAndroidKeyBoard) {
val imm: InputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0) //强制隐藏
}
}

三、开源地址。


目前项目已经开源,需要的朋友可以查看:github.com/AbnerMing88…


四、总结及注意事项。


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才会生效。


作者:二流小码农
链接:https://juejin.cn/post/7216143702125166653
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册