声明:语言表述能力不佳,缓慢提高中,给您带来不好的阅读体验请见谅。
今年google发布了jetpack compose beta版本,而且版本迭代频繁。感觉Google要真的推compose了。所以赶紧尝鲜试试,不过我也算晚的了。目前文档好像只有官方的还比较靠谱,有一些文章还不如不看。
在做登录页面时,需要实现 验证码输入框。如下所示
image
google提供的Textfield等等都不合适(强行适配也不是不可以,比如设定字符间隔)所以就打算自定义一个。(去他妈的onMeasure onLayout吧哈哈,直接画)
用canvas画,官方compose就是直接用底层api直接画出来的,按键输入找textinputservice拿。需要强调的是组件必须获取焦点才能显示出键盘。获取焦点可以使用pointerInput()(忘掉什么ontouch,onkeydown吧)。先上代码瞅一瞅。
//onCompleted函数是:输入完成后该干啥,一般是带上手机号去后台做验证。
@Composable
fun SmsCodeCompose(onCompleted: (String) -> Unit, modifier:Modifier=Modifier) {
//记住输入的值,compose重绘的时候会丢失,remember会在这个socpe范围内记住这个值
var text by remember{mutableStateOf("")}
//获取输入textInputService服务。 LocalTextInputService.current这个是compose库帮我们初始化的
val textInputService = LocalTextInputService.current
val focusRequester by remember { mutableStateOf(FocusRequester()) }
//这两个是自己搞的状态,其实可以不用
var focusState by remember { mutableStateOf(FocusState.Inactive) }
var isKeyboardShow by remember { mutableStateOf(false) }
//使用canvas 组件来实现,按键焦点都在modifier实现
Canvas(modifier = modifier
.focusRequester(focusRequester)
.onFocusChanged { it ->
if (it.isFocused) {
//保存焦点状态,方便用户隐藏键盘以后再弹出来
focusState = FocusState.Active
//开启按键输入
textInputService?.startInput(
//TextFieldValue 这个玩意还挺复杂的,在textfield 等组件可以直接通过他控制光标
value = TextFieldValue(""),
//设置键盘类型和右下角那个“确定”键的功能
imeOptions = ImeOptions.Default.copy(keyboardType = KeyboardType.Number,imeAction = ImeAction.Done),
//接收按键消息,所有键盘事件都是EditCommand接口的子类
onEditCommand = { command ->
//command 为啥是一个list,我猜测是可能有组合按键?遗留!!!!
command.forEach {
when (it) {
//用户隐藏了键盘
is FinishComposingTextCommand -> isKeyboardShow=false
//按键command,其中的text就是按下的键
is CommitTextCommand -> if(it.text.isDigitsOnly())text += it.text
//删除命令
is BackspaceCommand ->text=text.dropLast(1)
// else就懒得管了,还有不少command,比如移动输入光标的,等等
}
//输入完成
if(text.length==6)onCompleted(text)
}
},
//这个就是键盘右下角那个特殊的按键事件,比如搜索,完成,发送等等
onImeActionPerformed = {
if(it == ImeAction.Done ){
textInputService.hideSoftwareKeyboard()
}
}
)
}
}
.focusModifier()
//注册点击事件处理函数,在这边获取焦点
.pointerInput("key"){
detectTapGestures(onTap={
//请求焦点
focusRequester.requestFocus()
if(focusState==FocusState.Active && isKeyboardShow){
//显示键盘
textInputService?.showSoftwareKeyboard()
//这个状态其实无所谓,showSoftwareKeyboard调用多次也没关系
isKeyboardShow=true
}
})
}
) {
//开始画界面,额。。。大小是写死的,可以获取canvas的大小做成动态的。这就当作初版吧
drawIntoCanvas { canvas ->
//底线画笔
val grayPaint = Paint()
grayPaint.color = Gray400
grayPaint.style = PaintingStyle.Fill
//底线有焦点时候的画笔
val orangePaint = Paint()
orangePaint.color = Orange500
orangePaint.style = PaintingStyle.Fill
//文字画笔。
val textPaint = android.graphics.Paint()
//textPaint.strokeWidth=1f 设置描边可以加粗字体
textPaint.isAntiAlias = true
textPaint.style = android.graphics.Paint.Style.FILL_AND_STROKE
textPaint.color = Gray700.toArgb()
textPaint.textSize = 24.sp.toPx()
//坐标啥的就不多说了
val rectText = Rect()
var left = 0f
var right = 30.dp.toPx()
for (index in 0..5) {
canvas.drawRoundRect(
left,
38.dp.toPx(),
right,
40.dp.toPx(),
1.dp.toPx(),
1.dp.toPx(),
//根据输入文字的长度处理焦点。
if (index == text.lastIndex + 1) orangePaint else grayPaint
)
//draw 文字
if (index < text.length) {
textPaint.getTextBounds(text, index, index + 1, rectText)
canvas.nativeCanvas.drawText(
text,
index,
index + 1,
left + rectText.width().toFloat() / 2,
20.dp.toPx(),
textPaint
)
}
left += 40.dp.toPx()
right += 40.dp.toPx()
}
}
}
}
最终效果上个图,compose+kotlion写界面我觉得是真香。当然,你可以给焦点移动加上动画,那样会更好一点。这个组件绘制太多,有很多没必要的绘制,等优化好再更新一版
最终效果,这个是从layout Inspector截的,效果没那么好