#屠龙之技 Cocos Creator 项目中如何使用系统的文件选择对话框?
问题背景
文件选择对话框,在App开发中是很常用的一个组件,但是,在游戏开发中并不常见,所以Cocos Creator也没有提供这个组件。
但是,有没有办法在Cocos Creator的项目中使用这个组件呢?
答案当然是:有!
解决方法
方法1,直接调用操作系统的文件对话框组件。
不管是windows还是mac os,甚至android和ios,文件选择对话框,都是系统自带的组件。
所以,如果我们把这个组件的调用代码封装好,提供js接口,那么,就可以直接在Cocos Creator中使用了。
但是,这个方法就比较麻烦,需要针对不同的系统进行封装,费时费力。
方法2,使用HTML的文件对话框组件。
在Web编程时,我们可以使用file 类型的input组件,来作为文件选择对话框的入口。
<input type="file">
参考:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
在线示例:https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_input_type_file
Cocos Creator项目使用的是JavaScript语言,如果能直接调用HTML的接口,那么就可以省去了很多封装的工作。
有几个选择
选项 1). 静态添加
在index.html里添加file input组件,然后在Creator项目的JavaScript代码中调用。
选项 2). 动态添加
直接在Creator项目的JavaScript代码中创建file input组件,并使用。
选项2) 看起来更简洁一点,不需要修改index.html,而且这意味着不需要打包也可以进行测试。
步骤:
- 创建file input
// typescript 代码
function getInputBox(inputBoxId:string, containerId:string){
let inputBox = document.getElementById(inputBoxId) as HTMLInputElement;
if(!inputBox){
let container = document.getElementById(containerId);
if(!container){
container = document.createElement('div');
document.body.appendChild(container);
container.id = containerId;
}
inputBox = document.createElement("input") as HTMLInputElement;
inputBox.id = inputBoxId;
inputBox.type = "file";
container.appendChild(inputBox);
}
return inputBox;
}
- 添加监听
每当用户选择了一个新文件,input会触发onchange事件,所以可以在这个事件里对选择的文件进行处理。
有两种方式可以处理,- 1). 读取event的参数: event.target.files
- 2). 直接访问input的value: inputBox.value
let inputBox = this.getInputBox(this.inputBoxId, this.containerId);
if(inputBox){
inputBox.onchange = (evt)=>{
console.info("===> input value change: ", evt);
const fileList = evt.target.files;
console.info("===> file list: ", fileList);
console.info("===> value: ", inputBox.value);
let fileUrl = fileList[0];
if(!fileUrl){
console.info("===> No file selected");
return;
}
this.readFile(fileUrl);
};
}else{
console.warn("Can't get or create input box");
}
- 读取文件
获取文件的路径后,可以用fileReader读取文件信息
readFile(fileUrl){
let reader = new FileReader();
reader.onload = function(e) {
let content = e.target.result;
// Display file content
console.info("===> file content: ", content);
};
// 这里用文本方式读,也可以用别的方式
reader.readAsText(fileUrl);
}
- 触发点击
现在,一切准备就绪,只需要触发file input的click方法,就可以打开文件选择对话框了。
注意 需要把click方法的调用,绑定到Cocos Creator按钮组件的点击事件中。否则,浏览器会提示以下这个错误警告,并拒绝打开对话框:
File choose dialog can only be shown with a user activation
let inputBox = this.getInputBox(this.inputBoxId, this.containerId);
if(inputBox){
// 模拟点击按钮,打开对话框。
inputBox.click();
}
注意事项
使用HTML的组件,会有一些限制,比如
- 1). 只能在浏览器中使用。对于原生App,由于Creator打包的原生应用,运行底层不是浏览器组件,第二种方法应该是不可行的(未验证)。
- 2). 必须绑定到玩家的点击操作,不能在没有任何操作时,直接用代码弹窗。
完成
方法2的完整代码
https://gist.github.com/zhangzhibin/a21ec65b434f45efec2103f03b29ba57.js
const {ccclass, property} = cc._decorator;
@ccclass
export default class FileBox extends cc.Component {
// LIFE-CYCLE CALLBACKS:
@property
containerId = "_filebox_container_";
@property
inputBoxId = "_filebox_input_";
inputBox = null;
start () {
this.initInputBox();
}
initInputBox(){
let inputBox = this.getInputBox(this.inputBoxId, this.containerId);
if(inputBox){
inputBox.onchange = (evt)=>{
console.info("===> input value change: ", evt);
const fileList = evt.target.files;
console.info("===> file list: ", fileList);
console.info("===> value: ", inputBox.value);
let file = fileList[0];
if(!file){
console.info("===> No file selected");
return;
}
this.readFile(file);
};
}else{
console.warn("Can't get or create input box");
}
}
getInputBox(inputBoxId:string, containerId:string){
if(!this.inputBox){
let inputBox = document.getElementById(inputBoxId) as HTMLInputElement;
if(!inputBox){
let container = document.getElementById(containerId);
if(!container){
container = document.createElement('div');
document.body.appendChild(container);
container.id = containerId;
}
inputBox = document.createElement("input") as HTMLInputElement;
inputBox.id = inputBoxId;
inputBox.type = "file";
container.appendChild(inputBox);
}
this.inputBox = inputBox;
}
return this.inputBox;
}
onClick(){
if(this.inputBox){
console.info("click start");
this.inputBox.click();
console.info("click done")
}
}
readFile(fileUrl){
let reader = new FileReader();
reader.onload = function(e) {
let content = e.target.result;
// Display file content
console.info("===> file content: ", content);
};
// 这里用文本方式读,也可以用别的方式
reader.readAsText(fileUrl);
}
}