#夏日挑战赛#【FFH】JS自定义组件:DIY一个随点随用的键盘!(一) 原创 精华

FFH_PETSJ
发布于 2022-7-25 19:22
浏览
1收藏

本文正在参加星光计划3.0–夏日挑战赛

ArkUI自定义组件实战开发:如何快速拥有一个私人定制的软键盘?

前言

平时在Devco Studio调试代码的过程中经常需要输入的操作,像一些根据用户输入来处理数据的模块,然而相信不少的开发伙伴们都苦于预览器previewer中没有自带的输入法键盘,要么运行真机模拟器,要么自己在代码中初始化数据,不太自由和方便。
因此,在看完文档的自定义组件开发后,我决定写一个类似输入法键盘的自定义组件,随点随用。
这个夏天,我要实现输入自由!(来自前端开发人的高呼)
官方文档:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-components-custom-basic-usage-0000001281361086

效果展示

#夏日挑战赛#【FFH】JS自定义组件:DIY一个随点随用的键盘!(一)-鸿蒙开发者社区

代码实现

代码结构
#夏日挑战赛#【FFH】JS自定义组件:DIY一个随点随用的键盘!(一)-鸿蒙开发者社区

编写自定义组件的事件

理论解释:

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多数据输入场景的调试当中?那就需要加入更多的自定义组件特性功能,所以在下篇中进行了代码的改进:

JS自定义组件:DIY一个随点随用的键盘!(一)

总结

至此,一个属于你自己、随点随用的DIY键盘就诞生了。
总而言之,自定义组件的使用体验和封装效果很不错,耦合性可以有效的减少,对于前端UI的开发来说确实方便不少。当然,这次实战只是一个对自定义组件强大功能的简单试探,希望以后能用它创造出更丰富好用的组件!

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
liteKeyboard.zip 3.46M 20次下载
已于2022-7-26 08:50:13修改
6
收藏 1
回复
举报
1条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

预览器不自带键盘是真的难受,也可能是因为安全问题?总之感谢楼主提供方法。

回复
2022-7-26 11:22:27
回复
    相关推荐