石柱網(wǎng)站開發(fā)品牌推廣活動(dòng)有哪些
OpenHarmony最近一段時(shí)間,簡(jiǎn)直火的一塌糊度,學(xué)習(xí)OpenHarmony相關(guān)的技術(shù)棧也有一段時(shí)間了,做個(gè)記賬本小應(yīng)用,將所學(xué)知識(shí)點(diǎn)融合記錄一下。
1、記賬本涉及知識(shí)點(diǎn)
- ??? 基礎(chǔ)組件(Button、Select、Text、Span、Divider、Image)、容器(Row、Flex、List、Grid、Column)、定位(position)、路由(router)、Toast(promptAction)、Web組件;
- ??? 自定義彈窗(@CustomDialog);
- ??? 分布式鍵值數(shù)據(jù)庫(kù)(@ohos.data.distributedKVStore);
- ??? Web組件;
- ??? OpenHarmony三方庫(kù);
2、效果預(yù)覽
??
??
3、功能點(diǎn)實(shí)現(xiàn)簡(jiǎn)介
3.1?自定義彈窗
// 自定義彈窗定義
@CustomDialog
struct CustomDialogSetting {// 雙向綁定傳值@Link settingBudgetValue: string// 彈窗控制器,控制打開/關(guān)閉,必須傳入,且名稱必須為:controllercontroller: CustomDialogController// 彈窗中的按鈕事件cancel: () => voidconfirm: () => void// 彈窗中的內(nèi)容描述build() {Column() {Text($r('app.string.budget_setting')).fontSize(18).fontWeight(FontWeight.Bold).margin(12).textAlign(TextAlign.Center).width("100%")TextInput({placeholder: $r('app.string.estimated_amount_tips'),text: this.settingBudgetValue}).type(InputType.Number).height(60).width('90%').onChange((value: string) => {this.settingBudgetValue = value})Flex({ justifyContent: FlexAlign.SpaceAround }) {Button($r('app.string.cancel')).onClick(() => {this.settingBudgetValue = ''this.controller.close()this.cancel()}).backgroundColor(0xffffff).fontColor(Color.Black)Button($r('app.string.confirm')).onClick(() => {this.controller.close()this.confirm()}).backgroundColor(0xffffff).fontColor(AccountBookConstant.FONT_COLOR_BLUE)}.margin(15)}}
}
// 使用自定義彈窗
dialogController: CustomDialogController = new CustomDialogController({builder: CustomDialogSetting({cancel: this.onCancel.bind(this),confirm: this.onAccept.bind(this),settingBudgetValue: $settingBudgetValue}),cancel: this.onCancel,autoCancel: true,alignment: DialogAlignment.Center,gridCount: 4,customStyle: false
})// 開啟彈窗
this.dialogController.open()
3.2?懸浮按鈕
// 設(shè)置按鈕, 通過position進(jìn)行絕對(duì)定位
Button({ stateEffect: true }){Image($rawfile('setting.svg')).width(22).height(22)
}.width(42).height(42)
.borderRadius(90)
.shadow({ radius: 10, color: Color.Gray, offsetX: 5, offsetY:5 })
.position({ x: '98%', y: '98%' })
.markAnchor({ x: '98%', y: '98%'})
.margin(10).backgroundColor('#67C23A')
.onClick(() => {if (this.dialogController != undefined) {this.dialogController.open()}
})
3.3??數(shù)據(jù)存儲(chǔ)
// 定義鍵值對(duì)存儲(chǔ)類import distributedKVStore from '@ohos.data.distributedKVStore';const BUNDLE_NAME = "baseInfo"let context = getContext(this)
// 數(shù)據(jù)庫(kù)對(duì)象
let kvManager: distributedKVStore.KVManager | undefined = undefined;
// KVStore數(shù)據(jù)庫(kù)
let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;class DistributedUtil {constructor() {this.createKeyValueDB();}async getKvManager(bundleName?: string) {const kvStoreConfig: distributedKVStore.KVManagerConfig = {context: context,bundleName: bundleName || BUNDLE_NAME};try {kvManager = distributedKVStore.createKVManager(kvStoreConfig);}catch (err) {console.error(`error:${err}`)}}// 創(chuàng)建并得到指定類型的KVStore數(shù)據(jù)庫(kù)async createKeyValueDB(op?: distributedKVStore.Options) {if (!kvManager) {await this.getKvManager();}try {const options: distributedKVStore.Options = {// 當(dāng)數(shù)據(jù)庫(kù)文件不存在時(shí)是否創(chuàng)建數(shù)據(jù)庫(kù),默認(rèn)為truecreateIfMissing: true,// 設(shè)置數(shù)據(jù)庫(kù)文件是否加密,默認(rèn)為false,即不加密encrypt: false,// 設(shè)置數(shù)據(jù)庫(kù)文件是否備份,默認(rèn)為true,即備份backup: false,// 設(shè)置數(shù)據(jù)庫(kù)文件是否自動(dòng)同步。默認(rèn)為false,即手動(dòng)同步autoSync: true,// kvStoreType不填時(shí),默認(rèn)創(chuàng)建多設(shè)備協(xié)同數(shù)據(jù)庫(kù)kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,// 多設(shè)備協(xié)同數(shù)據(jù)庫(kù):kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION,securityLevel: distributedKVStore.SecurityLevel.S1};kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', op || options, (err, store: distributedKVStore.SingleKVStore) => {if (err) {console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in getting KVStore.');kvStore = store;});} catch (e) {console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);}return kvStore;}// 刪除指定鍵值的數(shù)據(jù)async deleteStoreData(key: string) {if (!kvStore) {return;}try {kvStore.delete(key, (err) => {if (err !== undefined) {console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in deleting data.');});} catch (e) {console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);}}// 向鍵值數(shù)據(jù)庫(kù)中插入數(shù)據(jù)async putStoreData(key: string, value: any) {if (!key || !value) {return}if(!kvStore) {kvStore = await this.createKeyValueDB();}try {kvStore.put(key, value, (err) => {if (err !== undefined) {console.error(`Failed to put data. Code:${err.code},message:${err.message}`);return;}console.info('Succeeded in putting data.');});} catch (e) {console.error(`putStoreData===>An unexpected error occurred. Code:${e.code},message:${e.message}`);}}// 獲取指定鍵的值async getStoreData(key: string) {if (!key) {return}if(!kvStore) {kvStore = await this.createKeyValueDB();}return new Promise((resolve, reject) => {try {kvStore.get(key, (err, data) => {if (err != undefined) {console.error(`Failed to get data. Code:${err.code},message:${err.message}`);reject(err)return;}resolve(data)});} catch (err) {reject(err)console.error('TAG-getStoreData', `Failed to get value, Cause: ${err}`)}});}
}export default new DistributedUtil();
// 使用鍵值對(duì)存儲(chǔ)
import distributedUtil from '../../common/distributedStrong'
// 1、增加
distributedUtil.putStoreData('amountRecords', JSON.stringify(dataArray))
// 2、 獲取
distributedUtil.getStoreData('amountRecords').then((res: string) => {if(res) {const result = JSON.parse(res)// 處理存儲(chǔ)的圖片資源,會(huì)自動(dòng)轉(zhuǎn)換為id的形式,無(wú)法直接獲取展示result.map(item => {item.icon = $rawfile(item.icon.params[0])return item})this.recordsArray = result}
})
3.4?統(tǒng)計(jì)圖表
3.4.1?定義本地html文件
在resources下創(chuàng)建rawfile文件夾,增加chart.html文件
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>柱狀圖示例</title><script src="./js/echarts5.4.0.js"></script>
</head>
<body>
<h1>本月支出</h1>
<div id="chartBarContainer" style="width: 400px; height: 300px;"></div><div id="chartPieContainer" style="width: 400px; height: 300px;"></div></body>
<script>function initBarChart(chartData) {const data = JSON.parse(chartData);var chartContainer = document.getElementById('chartBarContainer');var chart = echarts.init(chartContainer);var option = {tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'}},xAxis: {type: 'category',data: data.xAxisData},yAxis: {type: 'value'},series: [{data: data.seriesData,type: 'bar',showBackground: true,stack: 'Total',label: {show: true,position: 'top'},emphasis: {itemStyle: {color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#2378f7' },{ offset: 0.7, color: '#2378f7' },{ offset: 1, color: '#83bff6' }])}},itemStyle: {borderRadius: [25, 25, 0, 0],color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#83bff6' },{ offset: 0.5, color: '#188df0' },{ offset: 1, color: '#188df0' }])}}]};chart.setOption(option);}function initPieChart(chartData) {const data = JSON.parse(chartData);var chartContainer = document.getElementById('chartPieContainer');var chart = echarts.init(chartContainer);var option = {tooltip: {trigger: 'item'},legend: {show: false,top: '5%',left: 'center'},series: [{data: data.seriesData,type: 'pie',radius: ['40%', '70%'],avoidLabelOverlap: false,itemStyle: {borderRadius: 10,borderColor: '#fff',borderWidth: 2},startAngle: 180,labelLine: {show: true,length: 20, // 標(biāo)簽線的長(zhǎng)度length2: 50 // 標(biāo)簽線的第二段長(zhǎng)度},emphasis: {label: {show: true,fontSize: 40,fontWeight: 'bold'}},labelLine: {show: false}}]};chart.setOption(option);}
</script>
</html>
3.4.2?Web組件使用本地文件
import web_webview from '@ohos.web.webview'@Entry
@Component
struct Chart {controllerWeb: web_webview.WebviewController = new web_webview.WebviewController()build() {Web({ src: $rawfile('barChart.html'), controller: this.controllerWeb })}
}
3.4.3?Web組件向H5頁(yè)面?zhèn)髦?#xff0c;調(diào)用H5中的方法
// 初始化柱狀圖
const codeJS = `initBarChart('${JSON.stringify(this.chartBarData)}')
`
this.controllerWeb.runJavaScript(codeJS)
3.4.4?完整調(diào)用代碼
import web_webview from '@ohos.web.webview'
import router from '@ohos.router';interface ChartDataType {xAxisData?: Array<string | number>;seriesData?: Array<string | number | any>;
}@Entry
@Component
struct BarChart {controllerWeb: web_webview.WebviewController = new web_webview.WebviewController()private chartBarData: ChartDataType = {xAxisData: ['餐飲', '購(gòu)物', '教育', '生活', '寵物', '運(yùn)動(dòng)', '娛樂', '其他'],seriesData: [10, 20, 15, 30, 10, 20, 15, 30],}private chartPieData: ChartDataType = {seriesData: [{ value: 10, name: '餐飲' },{ value: 20, name: '購(gòu)物' },{ value: 15, name: '教育' },{ value: 30, name: '生活' },{ value: 10, name: '寵物' },{ value: 20, name: '運(yùn)動(dòng)' },{ value: 15, name: '娛樂' },{ value: 30, name: '其他' },],}build() {Column() {Row() {Button() {Image($rawfile('icon_back.png')).width(18)}.backgroundColor(Color.Transparent).padding(10).onClick(() => router.back())Text('圖表分析').fontSize(20).fontWeight(FontWeight.Bold)}.padding(10).justifyContent(FlexAlign.SpaceBetween).width('100%')Web({ src: $rawfile('barChart.html'), controller: this.controllerWeb }).verticalScrollBarAccess(true).javaScriptAccess(true).onPageEnd(() => {// 初始化柱狀圖const codeJS = `initBarChart('${JSON.stringify(this.chartBarData)}')`this.controllerWeb.runJavaScript(codeJS)// 初始化餅圖const codeJSPie = `initPieChart('${JSON.stringify(this.chartPieData)}')`this.controllerWeb.runJavaScript(codeJSPie)})}.width('100%').height('100%')}
}
3.4.5?傳值注意點(diǎn)總結(jié)
- 傳遞數(shù)據(jù)需要通過 JSON.stringify()?轉(zhuǎn)換為字符串;
- 傳遞的參數(shù)必須使用引號(hào)包裹,否則無(wú)法調(diào)用到H5中的方法;
- H5中使用傳過來的數(shù)據(jù),同理,需要使用 JSON.parse()?進(jìn)行轉(zhuǎn)換;
3.5??自定義鍵盤
使用Grid布局,通過rowStart、rowEnd、columnStart、columnEnd進(jìn)行單元格合并?;蛘呤褂胏olumn和row布局,循環(huán)即可。
參考:https://gitee.com/harmonyos/codelabs/tree/master/SimpleCalculator
Grid() {GridItem() {this.GridItemButtonBuilder('7')}.gridItemStyle().onClick(() => { this.clickBtn(7) })GridItem() {this.GridItemButtonBuilder('8')}.gridItemStyle().onClick(() => { this.clickBtn(8) })GridItem() {this.GridItemButtonBuilder('9')}.gridItemStyle().onClick(() => { this.clickBtn(9) })GridItem() {Text(this.time).backgroundColor(Color.White).width('100%').height('100%').textAlign(TextAlign.Center)}.gridItemStyle()GridItem() {this.GridItemButtonBuilder('4')}.gridItemStyle().onClick(() => { this.clickBtn(4) })GridItem() {this.GridItemButtonBuilder('5')}.gridItemStyle().onClick(() => { this.clickBtn(5) })GridItem() {this.GridItemButtonBuilder('6')}.gridItemStyle().onClick(() => { this.clickBtn(6) })GridItem() {this.GridItemButtonBuilder('remove')}.gridItemStyle().onClick(() => { this.clickBtn('remove') })GridItem() {this.GridItemButtonBuilder('1')}.gridItemStyle().onClick(() => { this.clickBtn('1') })GridItem() {this.GridItemButtonBuilder('2')}.gridItemStyle().onClick(() => { this.clickBtn('2') })GridItem() {this.GridItemButtonBuilder('3')}.gridItemStyle().onClick(() => { this.clickBtn('3') })GridItem() {this.GridItemButtonBuilder('保存', '#409EFF')}.rowStart(2).rowEnd(3).columnStart(3).columnEnd(4).onClick(() => { this.clickBtn('save') })GridItem() {this.GridItemButtonBuilder('清空')}.gridItemStyle().onClick(() => { this.clickBtn('clear') })GridItem() {this.GridItemButtonBuilder('0')}.gridItemStyle().onClick(() => { this.clickBtn('0') })GridItem() {this.GridItemButtonBuilder('.')}.gridItemStyle().onClick(() => { this.clickBtn('.') })
}
.height(220)
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr')
.rowsGap(0)
.margin({ top: 0 })
4、擴(kuò)展(如何使用基礎(chǔ)組件自定義柱狀圖)
- 使用Stack容器進(jìn)行堆疊
- 通過Rect繪制柱子
- 通過Divider繪制分割線
????一個(gè)簡(jiǎn)單的柱狀圖就完成了,具體可以參考健康生活,希望ArkUI可以早日集成Echarts類似的圖表組件,JS版本的有Chart組件,ArkTs的還未集成,期待官方??????
@Builder
content(item: OneMealStatisticsInfo) {Column() {if (item.totalFat > 0) {Rect({ width: 14, height: item.totalFat / 200 + 14, radius: 7 }).fill('#FD9A42').padding({ top: 14 }).margin({ bottom: -28 })}if (item.totalProtein > 0) {Rect({ width: 14, height: item.totalProtein / 200 + 14, radius: 7 }).fill('#FBD44E').padding({ top: 14 }).margin({ bottom: -21 })}if (item.totalCarbohydrates > 0) {Rect({ width: 14, height: item.totalCarbohydrates / 200 + 14, radius: 7 }).fill('#73CD57').padding({ top: 7 }).margin({ bottom: -7 })}}.clip(true)}@Builder
legendComponent(item: HistogramLegend) {Text(item.value).fontSize(12).fontColor('#18181A').fontFamily('HarmonyHeTi')
}@Component
struct Histogram {@Consume("dietData") dietData: Array<OneMealStatisticsInfo>@BuilderParam content?: (item: OneMealStatisticsInfo) => void@BuilderParam legendComponent?: (item: HistogramLegend) => voidprivate title: string | Resource = ''private legend: HistogramLegend[] = []build() {Column() {Text(this.title).textAlign(TextAlign.Start).fontSize(24).fontColor('#000000').fontFamily('HarmonyHeTi-Medium').width('100%').height(46)Stack({ alignContent: Alignment.Bottom }) {Column() {ForEach([0, 0, 0, 0, 0, 0], () => {Divider().strokeWidth(1).color('#D8D8D8')})}.height('100%').margin({ top: 20 }).justifyContent(FlexAlign.SpaceBetween)Column() {Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceEvenly, alignItems: ItemAlign.Start }) {ForEach(this.dietData, (item: OneMealStatisticsInfo) => {if (item.mealFoods.length > 1) {Column() {if (this.content !== undefined) {this.content(item)}Text(item.mealTime.name).fontSize(14).fontColor('#7E7E7E').fontFamily('HarmonyHeTi').margin({ top: 10 })}.justifyContent(FlexAlign.End).height('100%')}})}}.height(236)}.height(190)Row() {ForEach(this.legend, (item: HistogramLegend) => {Row() {Rect({ width: 9, height: 9, radius: 9 }).fill(item.color).margin({ right: 18 })if (this.legendComponent !== undefined) {this.legendComponent(item)}}})}.justifyContent(FlexAlign.SpaceEvenly).width('100%').margin({ top: 70 })}.height('100%').padding({ left: 32, right: 32 }).borderRadius(12).backgroundColor('#FFFFFF')}
}
后面計(jì)劃基于canvas基礎(chǔ)組件實(shí)現(xiàn)一個(gè)柱狀圖,不斷學(xué)習(xí),期望鴻蒙不斷強(qiáng)大完善。