JetPack compose 自定义输入框

声明:语言表述能力不佳,缓慢提高中,给您带来不好的阅读体验请见谅。

今年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截的,效果没那么好
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容