目前CocosCreator的最新版本2.0.4提供的输入框组件cc.EditBox没有自定义字体的功能,而且文字也不能居中显示,为了实现这些功能,可以在cc.EditBox的基础上再包装一层,把原本cc.EditBox的文字隐藏,在其上方放置一个文本标签,通过监听cc.EditBox的输入事件来设置文本标签的内容。而文本标签cc.Label是可以设置字体以及实现对齐方式的,故而可以此来实现输入框的这些效果。
下载
实现
下面介绍一下主要的实现过程。
隐藏原本的cc.EditBox
新的输入框的实现借助了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,故这个问题还没有很优雅的解决方案。