#夏日挑战赛#【FFH】JS自定义组件:DIY一个随点随用的键盘!(一) 原创 精华
ArkUI自定义组件实战开发:如何快速拥有一个私人定制的软键盘?
前言
平时在Devco Studio调试代码的过程中经常需要输入的操作,像一些根据用户输入来处理数据的模块,然而相信不少的开发伙伴们都苦于预览器previewer中没有自带的输入法键盘,要么运行真机模拟器,要么自己在代码中初始化数据,不太自由和方便。
因此,在看完文档的自定义组件开发后,我决定写一个类似输入法键盘的自定义组件,随点随用。
这个夏天,我要实现输入自由!(来自前端开发人的高呼)
官方文档:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-components-custom-basic-usage-0000001281361086
效果展示
代码实现
代码结构
编写自定义组件的事件
理论解释:
liteKeyboard.js代码中,自定义组件接口会提供三个基本的接口函数:
data(){}
props:{}
computed(){}
这里只需使用data和props,(computed用于数据预处理,详情可以去官方文档看)
props
自定义组件可以通过props声明属性,父组件通过设置属性向子组件传递参数,props支持类型包括:String,Number,Boolean,Array,Object,Function。
有两种书写方式
props:{
textType:{
default : 'defaultValue'
}
} //---第一种
props : ['textType'] //---第二种
第一种的方式可以给props规定的属性设置默认值(通过关键词default)
props使用演示
liteKeyboard.js
props: {
isValid:{
default: 'true'
},
},
index.hml
<element name="liteKeyboard" src="../../common/component/liteKeyboard.hml"></element>
<div class="container">
<litekeyboard is-valid="false" @text-type="textClick"></litekeyboard>
</div>
这里的is-valid属性即是上面设置的isValid,is-valid的值将作为参数传递给isValid
注意: 驼峰命名法的 prop 名,在外部父组件传递参数时需要使用短横线分隔命名形式,即当属性isValid在父组件引用时需要转换为is-value。
data
类似pages页面的data:{},功能除了初始化hml绑定的数据,还可以用来接收父组件(即调用它的组件)的参数
因为自定义组件的数据是单向性的(父传子),子组件即liteKeyboard.js不能直接修改父组件传来的值
引用官方文档:
数据单向性*
父子组件之间数据的传递是单向的,只能从父组件传递给子组件,子组件不能直接修改父组件传递下来的值,可以将props传入的值用data接收后作为默认值,再对data的值进行修改。
data使用演示:
export default {
props: {
isValid:{
default: 'true'
},
},
data() {
return {
inputText: [],
bit: false,
isValid: this.isValid //---本地定义isValid接收props传递的参数。再进行修改-
}
},
除了这三个基本接口,自己也可以自定义事件绑定
通过 this.$emit(‘绑定的函数名’, params:参数) 绑定
liteKeyboard.js
this.$emit('textType', {
text : texts
});
index.js
textClick(e){
this.inputText = e.detail.text //---通过e.details.获取$emit传递的参数
console.info('receive text = '+this.inputText)
},
index.hml
<litekeyboard is-valid="false" @text-type="textClick"></litekeyboard> //驼峰命名也遵守上面提到的命名规则
理论结束,实战开始
大体思路:
对于数字键盘点击输入的数字用字符数组存储,点击事件触发时将字符数组作为参数text传递,再index.js用e.detail.text获取,并在自定义事件中与把所有需要输入的文本框数据进行绑定。
先写好键盘的样式
对于这种规则的宫格布局,采用grid来布局是不错的方法
liteKeyboard.hml
<div class="container">
<div class="keyboardCot">
<div class="grid-container">
<div class="grid-item" id="1" @click="click">
<text class="keyboard">1</text>
</div>
<div class="grid-item" id="2" @click="click">
<text class="keyboard">2</text>
</div>
<div class="grid-item" id="3" @click="click">
<text class="keyboard">3</text>
</div>
<div class="grid-item" id="4" @click="click">
<text class="keyboard">4</text>
</div>
<div class="grid-item" id="5" @click="click">
<text class="keyboard">5</text>
</div>
<div class="grid-item" id="6" @click="click">
<text class="keyboard">6</text>
</div>
<div class="grid-item" id="7" @click="click">
<text class="keyboard">7</text>
</div>
<div class="grid-item" id="8" @click="click">
<text class="keyboard">8</text>
</div>
<div class="grid-item" id="9" @click="click">
<text class="keyboard">9</text>
</div>
<div class="grid-item" id="." @click="click">
<text class="keyboard">.</text>
</div>
<div class="grid-item" id="0" @click="click">
<text class="keyboard">0</text>
</div>
<div class="grid-item" id="x" @click="click">
<image src="/common/images/ic_contacts_delete.png"></image>
</div>
</div>
</div>
</div>
liteKeyboard.css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: black;
}
.keyboardCot {
top: 8%;
height: 560px;
width: 660px;
justify-content: space-around;
border-radius: 40px;
}
.grid-container {
height: 100%;
width: 100%;
display: grid;
grid-template-rows: 140px 140px 140px 140px;
grid-template-columns: 220px 220px 220px;
}
.grid-item {
background-color: rgba(41, 41, 41, 1);
border-radius: 100px;
width: 100%;
height: 95%;
margin: 5px;
justify-content: space-around;
align-content: space-around;
align-items: center;
}
.grid-item:active {
background-color: darkgray;
}
.keyboard {
font-size: 60px;
font-weight: 300;
color: white;
}
.grid-item > image {
height: 80px;
width: 80px;
}
编写响应事件
liteKeyboard.js
export default {
props: {
isValid:{
default: 'true'
},
},
data() {
return {
inputText: [],//---存储键盘输入数据的字符数组
bit: false,
isValid: this.isValid //--- is-valid属性(是否需要数据合法性处理)
}
},
click(e) { //----数字键盘点击事件
let id = e.target.id
if (this.isValid === 'false') { //---不用合法处理
this.randomInput(id).then(texts => {
this.$emit('textType', {
text: texts
});
})
}
else if (this.isValid === 'true') {
this.validInput(id).then(texts => {
this.$emit('textType', {
text: texts
});
})
}
else {
console.info(`attribute "isValid" error , "isValid" only can be false | true`) //---限定is-valid只有两种参数false和true
}
},
randomInput(id) { //---不需要合法性检查时
return new Promise((res) => {
if (id !== 'x') {
this.inputText += `${id}`
res(this.inputText)
} else {
if (this.inputText.length !== 0) {
this.inputText = this.inputText.slice(0, this.inputText.length - 1)
}
res(this.inputText)
}
})
},
validInput(id) { //---合法性检查和处理
return new Promise((res) => {
let len = this.inputText.length
if (id === '.') {
let a = this.inputText.indexOf('.')
if (a !== -1)this.bit = true
else this.bit = false
if (this.bit || (len === 2 && this.inputText[0] === '0' && this.inputText[1] === '.'))res(this.inputText)
}
if (len === 1) {
if (this.inputText[0] === '0' && id !== '.' && id !== 'x')res(this.inputText)
}
if (id === 'x') {
console.info('x')
if (len !== 0) {
this.inputText = this.inputText.slice(0, len - 1)
res(this.inputText)
}
}
if (len <= 7) {
if (id !== 'x') {
this.inputText += `${id}`
res(this.inputText)
}
}
res(this.inputText)
})
}
}
父组件界面(配合dialog弹窗食用更佳!)
index.hml
<element name="liteKeyboard" src="../../common/component/liteKeyboard.hml"></element>
<div class="container">
<div class="titleCot" onclick="showKeyboard">
<div class="titleText">
<text style="
flex-wrap : wrap;
justify-content : space-around;
height : 200px;
color : white;
font-size : 90px;" >{{inputText}}
</text> <!--换成input文本框会与真机的输入法冲突。。-->
</div>
</div>
<dialog style="width : 100%; height : 800px; position : fixed;" id="keyboard">
<litekeyboard is-valid="false" @text-type="textClick"></litekeyboard>
</dialog>
</div>
export default {
data: {
inputText:''
},
onInit() {
},
textClick(e){ //---自定义事件(点击)
this.inputText = e.detail.text //---在这里进行数据绑定
console.info('receive text = '+this.inputText)
},
showKeyboard(){ //---带输入文本框组件的点击事件
this.$element('keyboard').show()//---当点击待输入的文本框时就拉起键盘
},
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 100%;
height: 100%;
background-color: black;
}
.titleText {
text-align: center;
height: 200px;
}
.titleCot {
position: absolute;
width: 100%;
height: 200px;
align-content: space-around;
background-color: gray;
align-items: center;
justify-content: space-around;
}
为了体现自定义组件特性和丰富liteKeyboard的功能,我加入了props属性:is-valid来根据实际是否需要对输入数据进行合法性检查,默认值为false不需要,这在对于一些规定数据类型为Number的场景比如金额输入很适用,相当于实现了像手机输入法的一些功能。当然,我们还可以借助props、computed来DIY更多的功能,充分开发自定义组件的特性,做出更高级的键盘。
待改进
上面编写的键盘不太实用,仅限绑定一个数据(即只能绑定一个文本输入框),如何应用到我们平时在previewer多数据输入场景的调试当中?那就需要加入更多的自定义组件特性功能,所以在下篇中进行了代码的改进:
总结
至此,一个属于你自己、随点随用的DIY键盘就诞生了。
总而言之,自定义组件的使用体验和封装效果很不错,耦合性可以有效的减少,对于前端UI的开发来说确实方便不少。当然,这次实战只是一个对自定义组件强大功能的简单试探,希望以后能用它创造出更丰富好用的组件!
预览器不自带键盘是真的难受,也可能是因为安全问题?总之感谢楼主提供方法。