目前CocosCreator的最新版本2.0.4提供的输入框组件cc.EditBox没有自定义字体的功能,而且文字也不能居中显示,为了实现这些功能,可以在cc.EditBox的基础上再包装一层,把原本cc.EditBox的文字隐藏,在其上方放置一个文本标签,通过监听cc.EditBox的输入事件来设置文本标签的内容。而文本标签cc.Label是可以设置字体以及实现对齐方式的,故而可以此来实现输入框的这些效果。
下面介绍一下主要的实现过程。
新的输入框的实现借助了cc.EditBox的输入事件,而它原本显示的文本我们要隐藏掉,以便在那个位置上显示我们自己的文本标签。为了隐藏掉cc.EditBox的文本,本人试过了很多方法,把其所在节点active设置false虽然可以隐藏但输入框也无法响应操作了,自然不可行;透明度设为0或是把其放置到背景下层,输入的时候文字还是会浮现在最上层(后查知这似乎是与CocosCreator原生组件永远最后渲染有关);最后发现把其节点的高度设置为0就能实现文字的隐藏,但这样的话将无法点击到输入框,即无法开始输入。
为了实现可以点击开始输入,查阅文档得知cc.EditBox有一个方法setFocus()。
setFocus 让当前 EditBox 获得焦点
当用户点击当前节点后调用这个方法可以强行开始输入,在手机端弹出键盘。 在CustomEditBox.js的onLoad中写入以下代码,以创建带cc.EditBox的节点:
//创建带输入框组件的节点。弹出及收起键盘、获取输入的文字,都是通过这个组件来实现的 let nodeEditBox = new cc.Node("EditBox"); nodeEditBox.width = this.node.width; //高度为0,以隐藏输入时显示的文字 nodeEditBox.height = 0; this.node.addChild(nodeEditBox, 0); //添加并设置输入框组件 let editBox = nodeEditBox.addComponent(cc.EditBox); editBox.inputMode = cc.EditBox.InputMode.SINGLE_LINE; editBox.maxLength = this.maxLength;然后监听触摸结束事件,在回调函数中调用setFocus开始输入。此时只能监听触摸结束事件,因为如果在触摸开始或触摸移动中调用setFocus,接下来一定触发触摸结束事件,而触摸结束事件触发时会使输入框失去焦点而使得输入结束,最终导致点击开始输入后立即就结束了。
//监听cc.EditBox组件产生的事件,以实现变更输入文字,结束输入的操作 this.node.on("touchend", () => { this._startEdit(); });首先在脚本文件的properties字段中声明一个字体资源的属性,以便用户可以在编辑器中拖拽字体资源来设置字体。
font: { displayName: "字体", type: cc.Font, default: null, //设置notify以在编辑器中实时预览设置效果 notify: function () { if (this.node) { //获取文字显示节点的Label组件并设置其字体 this.node.getChildByName("Text").getComponent(cc.Label).font = this.font; } } }关于更多的属性声明的内容可以参阅CocosCreator脚本属性在属性面板的显示
//创建文本显示节点。显示在输入框输入的文字,通过读取cc.EditBox组件的文字来更新自身文字内容 let nodeText = this.node.getChildByName("Text"); if (!nodeText) { //获取并判断名为Text的子节点是否存在是为了编辑器中不重复创建节点 nodeText = new cc.Node("Text"); this.node.addChild(nodeText, 1); //添加并设置Label组件 let label = nodeText.addComponent(cc.Label); //设置字体 if (this.font) { label.font = this.font; } }有了显示输入内容的节点,就可以在点击开始输入时显示闪烁的光标,以提示用户可以开始输入了。
为了实现在脚本的onLoad中创建光标,而不依赖任何外部图像资源,且要与字体颜色保持一致,可以创建一个Label的节点,其文字设置为一个特殊字符: █,即完全填充一个字符位置的色块。然后设置Label组件的overflow为 CLAMP,节点宽度为1。这样文字被截成宽度为1,高度为行高的字符,正好可以用来做光标。
而光标闪烁实现可以在脚本文件的update中更新光标节点的可见性。
ctor() { //光标节点的引用 this._cursor = null; //标记当前是否处于编辑状态 this._isEditing = false; //实现光标闪烁所需的计时器 this._blinkTimer = 0; }, //每帧更新光标的状态 update(dt) { if (!CC_EDITOR) { //编辑器下不更新光标 this._updateCursor(dt); } }, //实现光标闪烁 _updateCursor(dt) { if (!this._isEditing) { //不是编辑状态 this._cursor.active = false; return; } if (this._blinkTimer >= 0.5) { //计时器运行了0.5秒 //重置计时器 this._blinkTimer = 0; //反转光标节点可见性 this._cursor.active = !this._cursor.active; } else { //计时器还没到0.5秒 //计时器增加两帧之间的时间间隔 this._blinkTimer += dt; } }通过监听cc.EditBox的节点上的text-changed事件,可在输入文字改变时获取cc.EditBox的文字,以此来更新显示出来的文字。
//this._editBox为cc.EditBox的引用 this._editBox.node.on("text-changed", () => { //this._labelText为用于显示文字的Label组件的引用 this._labelText.string = this._editBox.string; } });至此,CustomEditBox已经可以实现点击开始输入文字,光标闪动,按下键盘可以看到输入文字的增加,但还没实现结束输入。结束输入可以监听上面代码中的this._editBox的editing-did-ended事件,在事件回调中隐藏光标,即表示结束输入了。 Android平台和微信小游戏平台会出现点击输入框之外无法结束输入,翻阅Cocos文档并没有发现可以主动失去焦点的API,故这个问题还没有很优雅的解决方案。