欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CocosCreator上实现自定义字体的输入框

程序员文章站 2022-04-26 17:45:43
...

目前CocosCreator的最新版本2.0.4提供的输入框组件cc.EditBox没有自定义字体的功能,而且文字也不能居中显示,为了实现这些功能,可以在cc.EditBox的基础上再包装一层,把原本cc.EditBox的文字隐藏,在其上方放置一个文本标签,通过监听cc.EditBox的输入事件来设置文本标签的内容。而文本标签cc.Label是可以设置字体以及实现对齐方式的,故而可以此来实现输入框的这些效果。

下载

  • 要预览输入框效果,可点击效果样例,下载后用CocosCreator打开运行。
  • 本人写的输入框脚本组件,下载后即可挂载到某个节点上即可使用,可在编辑器下运行。功能不完善,可能有bug,请指正。

实现

下面介绍一下主要的实现过程。

隐藏原本的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,故这个问题还没有很优雅的解决方案。