新聞中心
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

?? 開源基礎(chǔ)軟件社區(qū)??
??https://ost.??
前言
學(xué)習(xí)了關(guān)系型數(shù)據(jù)庫和一些相關(guān)的codelabs后,為了更深入地了解ArkUI關(guān)系型數(shù)據(jù)庫的使用和操作,我決定復(fù)刻一個小小的手機備忘錄。整個實現(xiàn)過程不僅有對關(guān)系型數(shù)據(jù)庫接口的嘗試封裝,還碰了各種UI實現(xiàn)、路由跳轉(zhuǎn)的壁,印象很深,所以就想分享一下這次復(fù)刻實現(xiàn)的過程,總結(jié)一下經(jīng)驗。由于寫的代碼量較大,下面主要說一下與數(shù)據(jù)庫通信的環(huán)節(jié)和路由通信的環(huán)節(jié),UI布局放在上傳的zip里。
部分效果展示
這是主界面(是不是有點像:-):
由于界面功能較多,更多的gif在后面功能拆分環(huán)節(jié)再來展示
代碼實現(xiàn)流程
代碼結(jié)構(gòu):
實現(xiàn)思路
首先把繁多的rdb數(shù)據(jù)庫接口封裝成幾個功能較完備的大接口,在UI界面進行調(diào)用和數(shù)據(jù)處理、數(shù)據(jù)通信。
一、關(guān)系型數(shù)據(jù)庫的封裝
官方對它的宣傳就是:不用學(xué)會sql也能用(確實對,但還是要有些數(shù)據(jù)庫基礎(chǔ)才能處理結(jié)果集resultSet和使用executeSql接口)。
導(dǎo)入包:
import data_rdb from ‘@ohos.data.rdb’。
創(chuàng)建數(shù)據(jù)庫:
data_rdb.getRdbStore返回一個數(shù)據(jù)庫管理對象。
|
參數(shù)名 |
類型 |
說明 |
|
context |
Context |
應(yīng)用的上下文。 |
|
config |
StoreConfig |
與此RDB存儲相關(guān)的數(shù)據(jù)庫配置。 |
|
version |
number |
數(shù)據(jù)庫版本。 |
眾所周知,數(shù)據(jù)庫有增刪改查四大操作,則它也提供了幾個常用接口。
|
api |
|
data_rdb.insert |
|
data_rdb.query |
|
data_rdb.delete |
|
data_rdb.update |
|
executeSql |
其中,插入操作只需一個用于存儲鍵值對的valueBucket就夠了,而刪除、更新和查詢都要借助RdbPredicates謂詞來獲取數(shù)據(jù)索引才能進一步操作:
|
RdbPredicates |
|
表示關(guān)系型數(shù)據(jù)庫(RDB)的謂詞。該類確定RDB中條件表達式的值是true還是false。 |
let predicates = new data_rdb.RdbPredicates(“EMPLOYEE”)//例如創(chuàng)建一個為"EMPLOYEE"表服務(wù)的predicates實例。
還有更多的跟分布式有關(guān)的接口:
|
setDistributedTables |
|
obtainDistributedTableName |
|
sync |
還需要簡單了解一下結(jié)果集的使用。
??結(jié)果集文檔??
下面是看一些codelabs后進一步封裝成的rdbStoreServer接口。
|
操作 |
接口名 |
|
插入 |
insertValue |
|
更新 |
updateValue |
|
列表查詢 |
queryValue |
|
限定詞查詢 |
search |
|
刪除 |
deleteValue |
封裝了五個接口,其中把查詢參數(shù)以及查詢得到的結(jié)果集從封裝中解耦出來,能更靈活的查詢數(shù)據(jù)和處理結(jié)果集。比如查詢:通過field、value、table(表名)三個參數(shù)查詢后獲得結(jié)果集后再回調(diào)處理,如下:
rdbStoreServer.js:
import featureAbility from '@ohos.ability.featureAbility'
import data_rdb from '@ohos.data.rdb'
const STORE_CONFIG = { name: "rdbStore.db" }
export default class rbdStoreModel {
rdbStore
#tableList = []
constructor(SQL_CREATE_TABLE_LIST) { //---在構(gòu)造時傳入準(zhǔn)備好的sql語句
for (let i in SQL_CREATE_TABLE_LIST) {//---可插入多條SQL語句,一個庫建多個表
this.#tableList.push(SQL_CREATE_TABLE_LIST[i])
}
}
createKvStore(cb) {
if (typeof (this.rdbStore) === 'undefined') {
let self = this;
let context = featureAbility.getContext(); //---獲取上下文
let promise = data_rdb.getRdbStore(context, STORE_CONFIG, 1)
promise.then((rdbStore) => {
self.rdbStore = rdbStore;
for (let i in this.#tableList) {
rdbStore.executeSql(this.#tableList[i], null);
}
console.info("xxx--- rdbStore " + 'create table.')
cb();
}).catch((err) => {
console.info("xxx--- rdbStore " + err)
cb();
})
} else {
cb();
}
}
insertValue(table, valueBucket) {
this.createKvStore(() => {
let promise = this.rdbStore.insert(table, valueBucket)
promise.then((rows) => {
console.info('xxx--- rdbStore.insert done ' + rows)
})
})
}
updateValue(valueBucket, table, field, value) {
this.createKvStore(() => {
console.info(`xxx--- rdbStore.update field = ${field} value = ${value} == ${JSON.stringify(valueBucket)}`)
let predicates = new data_rdb.RdbPredicates(table)
predicates.equalTo(field, value)
this.rdbStore.update(valueBucket, predicates, function (err, rows) {
console.info("xxx--- rdbStore.update updated row count: " + rows)
})
})
}
deleteValue(table, field, value,cb) {
this.createKvStore(() => {
console.info(`xxx--- rdbStore.delete field = ${field} value = ${value}`)
let predicates = new data_rdb.RdbPredicates(table)
predicates.equalTo(field, value)
this.rdbStore.delete(predicates, (err, rows) => {
if (rows === 0) {
console.info("xxx--- rdbStore.delete rows: -1")
}
else console.info("xxx--- rdbStore.delete rows: " + rows)
cb()
})
})
}
queryValue(table,callback) { //---所有columns值查找
this.createKvStore(() => {
let predicates = new data_rdb.RdbPredicates(table)
let promise = this.rdbStore.query(predicates)//---當(dāng)query沒有columns鍵值對集時默認(rèn)返回所有columns
console.info("xxx--- rdbStore query start")
promise.then((resultSet) => {
callback(resultSet);
})
})
}
search(table,field,value,callback){ //---限定詞查找
this.createKvStore(()=>{
let predicates = new data_rdb.RdbPredicates(table)
predicates.contains(field,value)
let promise = this.rdbStore.query(predicates)
console.info("xxx--- rdbStore search start")
promise.then((resultSet) => {
callback(resultSet);
})
})
}
}
index.js:
import router from '@ohos.router';
import rdbStore from '../../common/model/rdbServer'
const SQL_CREATE_TABLE = ["CREATE TABLE IF NOT EXISTS NOTES (ID INTEGER PRIMARY KEY AUTOINCREMENT, TITLE TEXT NOT NULL, CONTENT TEXT NOT NULL)"]
export default {
data: {
rdbStore: new rdbStore(SQL_CREATE_TABLE),
list: [],
length: 0,
},
onShow() {
this.query()
},
query() {
let self = this
this.rdbStore.queryValue('NOTES', (resultSet) => {//查找操作與查詢操作不同但對結(jié)果集處理相同
self.resultSetServer(resultSet)
})
},
onChange(e) {
let val = e.value
var self = this
console.info('xxx--- search value change ' + val)
this.rdbStore.search('NOTES', 'TITLE', val, (resultSet) => {//查找操作與查詢操作不同但對結(jié)果集處理相同
self.resultSetServer(resultSet)
})
}
resultSetServer(resultSet) { //---結(jié)果集處理
let contactList = []
var self = this
if (resultSet.rowCount > 0) {
while (resultSet.goToNextRow()) {
let id = resultSet.getLong(resultSet.getColumnIndex("ID"));
let title = resultSet.getString(resultSet.getColumnIndex("TITLE"));
let content = resultSet.getString(resultSet.getColumnIndex("CONTENT"));
let obj = {
id: id,
title: title,
content: content
};
contactList.push(obj);
}
if (contactList.length > 0) {
this.flag = true
self.list = contactList
this.length = contactList.length
console.info('xxx--- query suc length = ' + contactList.length)
}
} else {
console.info('xxx--- query empty')
this.length = 0
this.flag = false
}
resultSet.close();
resultSet = null;
},
}
二、主界面調(diào)用增刪改查
1、頁面初始化
兩級UI:
第一級頁面:
1、主界面index的UI是熟悉的組件list列表渲染和block的條件渲染,展示數(shù)據(jù)庫里的每條數(shù)據(jù)和提供增刪改查交互,這里不多說;
2、在onShow生命周期事件加入查詢操作,每一次頁面展示(添加和更新操作完成后跳轉(zhuǎn)回來時)都會查詢一次,以保證數(shù)據(jù)是最新的。
3、渲染時,單條數(shù)據(jù)分為標(biāo)題和內(nèi)容、標(biāo)題最大長度為5、內(nèi)容溢出的用冒號…來省略。
4、第一級頁面集成了刪除和查詢操作。
{{ $item.title }}
{{ $item.content }}
第二級頁面:
1、對單條數(shù)據(jù)的具體操作頁面,分為兩個操作:添加和改變數(shù)據(jù),需要區(qū)分。
2、UI:主要分為標(biāo)題框和文本框,由于文本框需要覆蓋全部的空余面積,就采用textarea——多行文本輸入的文本框組件,屬性和事件與input的text類型差不多,但是能點擊文本框任意位置喚起鍵盤。
??textarea組件鏈接??。
//--softkeyboardenabled屬性為編輯時是否彈出系統(tǒng)軟鍵盤
2、增code:1
增加和更新與主頁面之間的頁面通信依靠路由通信。
index.js:(onShow和路由跳轉(zhuǎn)放在后面的更新操作一起說)。
add() {
router.push({
url: 'pages/writeNotes/writeNotes',
params: {
info: {
title: '', //---新建,傳遞數(shù)據(jù)為空
content: '',
code: 1 //---1 代表為增加操作,對應(yīng)數(shù)據(jù)庫insert接口
}
}
})
},(順帶提一句:里面的黑色數(shù)字軟鍵盤是我上篇那個自定義組件,響應(yīng)比百度輸入法快一點我就用了)
3、改code:0
更新實現(xiàn)思路:
點擊特定區(qū)塊進入其第二級頁面,傳遞列表已經(jīng)渲染的好數(shù)據(jù)以供更新,除了頁面數(shù)據(jù)通信和調(diào)用數(shù)據(jù)庫接口外,其它與增加操作類似。因為更新操作需要額外的ID值來匹配數(shù)據(jù)庫進行更新,而增加只管疊加上去就行了。
路由跳轉(zhuǎn)
這里要用到路由頁面的跳轉(zhuǎn),在這碰了壁:router.back()和router.push()在傳參中用法都一樣,但是實際調(diào)用的時候我采用router.back({url,params})的方式時在index.js頁面卻接收不到傳遞回來的參數(shù),但router.push可以,而兩者又有很明顯的區(qū)別:back()是返回上一級頁面,那個頁面的數(shù)據(jù)仍然保留在后臺緩存;而push則會新建一個頁面,上級頁面的數(shù)據(jù)保留不了,也要手動用router.clear()清除之前舊的頁面,但也只能先采用router.push的方式。
index.js:
click(title, content, id) {
if (this.deleteListener === false) {
router.push({
url: 'pages/writeNotes/writeNotes',
params: {
info: {
id: id, //---傳遞列表中已有數(shù)據(jù):標(biāo)題和內(nèi)容,方便更新
title: title,
content: content,
code: 0 //---1 代表為更新操作,對應(yīng)數(shù)據(jù)庫update接口
}
}
})
}
},
onShow() {
let params = router.getParams() //---getParams方法接收路由傳參
if (params) {
let info = params.info
let code = info.code
let id = info.id
let title = info.title
let content = info.content
console.info(`xxx--- info receive ${code} ${title} ${content}`)
let obj = {
title: title,
content: content
}
if (code == 0) {
this.update(obj, id, () => {
setTimeout(() => { //---采用延時查詢,避免數(shù)據(jù)不同步
this.query()
}, 400)
})
}
else {
this.insert(obj, () => { //---采用延時查詢,避免數(shù)據(jù)不同步
setTimeout(() => {
this.query()
}, 400)
})
}
}
else this.query()
},writeNotes.js:
update() {
console.info(`xxx--- info submit ${this.code} ${this.title} ${this.inputText} `)
router.clear() //---清除之前多余的頁面
let info
if (this.code == 0) { //---更新操作時
info = {
id: this.id,
title: this.title,
content: this.inputText,
code: this.code
}
} else { //---增加操作時
info = {
title: this.title,
content: this.inputText,
code: this.code
}
}
router.push({
url: 'pages/index/index',
params: {
info: info
}
})
},
4、刪
刪除操作(支持多選):
刪除的操作比較簡單,就是支持多選的功能復(fù)雜一點。
刪除實現(xiàn)思路:
block控制復(fù)選框和其它按鈕的渲染,長按后顯示或隱藏。選中數(shù)據(jù)塊的id值存入待刪除列表deleteList,再通過delete接口逐個刪除,最后查詢列表、清空deleteList。
checkboxOnChange(id, e) { //---選擇刪除
if (e.checked) {
this.deleteList.push(id)
} else {
let index = this.deleteList.find(e => e === id)
this.deleteList.splice(index, 1)
}
},
deletePress(idx, id) {
this.isCheck = idx
this.deleteList.push(id)
this.deleteListener = true
},
deleteNotes() {
this.deleteListener = false
if (this.deleteList.length > 0) {
for (let i in this.deleteList) {
this.rdbStore.deleteValue('NOTES', 'ID', this.deleteList[i])
}
this.deleteList.splice(0)
setTimeout(() => { //---采用延時查詢,避免數(shù)據(jù)不同步
this.query()
}, 400)
}
},
5、查
查詢操作:
主要涉及到一些簡單的UI交互,只需調(diào)用封裝好的search接口即可,不多解釋,直接放圖。
總結(jié)
官方提供的關(guān)系型數(shù)據(jù)庫接口方便了很多沒深入學(xué)習(xí)sql語句的鴻蒙開發(fā)者。我在這次的開發(fā)嘗試當(dāng)中也發(fā)現(xiàn)了一些坑,比如文檔function back(options?: RouterOptions ):void寫著router.back()同樣支持params傳參但是獲取不到,或者說我操作不當(dāng),但是這方面的文檔說明不太夠,有點懵。但總之,關(guān)系型數(shù)據(jù)庫用起來感覺很順暢那就夠了,沒有被什么不老牌bug絆住。
分享文章:挑戰(zhàn)ArkUI復(fù)刻手機備忘錄(Rdb數(shù)據(jù)庫)
當(dāng)前路徑:http://fisionsoft.com.cn/article/dpdpijs.html


咨詢
建站咨詢
